some basic stuffs.
This commit is contained in:
@@ -65,7 +65,7 @@ func (b *EventBus) JoinPrivileged(service ActiveService) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *EventBus) RunCommand(command Command) {
|
func (b *EventBus) RunCommand(command Command) {
|
||||||
if cd := command.CommandDescription(); !strings.HasPrefix(cd, "SetStates") {
|
if cd := command.CommandDescription(); !strings.HasPrefix(cd, "SetState") {
|
||||||
if setStates := atomic.LoadInt32(&b.setStates); setStates > 0 {
|
if setStates := atomic.LoadInt32(&b.setStates); setStates > 0 {
|
||||||
fmt.Println("[INFO]", setStates, "SetStates commands hidden.")
|
fmt.Println("[INFO]", setStates, "SetStates commands hidden.")
|
||||||
atomic.AddInt32(&b.setStates, -setStates)
|
atomic.AddInt32(&b.setStates, -setStates)
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ type HardwareState struct {
|
|||||||
SupportFlags device.SupportFlags `json:"supportFlags"`
|
SupportFlags device.SupportFlags `json:"supportFlags"`
|
||||||
ColorFlags device.ColorFlags `json:"colorFlags"`
|
ColorFlags device.ColorFlags `json:"colorFlags"`
|
||||||
ColorGamut *color.Gamut `json:"colorGamut,omitempty"`
|
ColorGamut *color.Gamut `json:"colorGamut,omitempty"`
|
||||||
TemperatureRange *[2]int `json:"temperatureRange,omitempty"`
|
ColorKelvinRange *[2]int `json:"colorKelvinRange,omitempty"`
|
||||||
Buttons []string `json:"buttons"`
|
Buttons []string `json:"buttons"`
|
||||||
State device.State `json:"state"`
|
State device.State `json:"state"`
|
||||||
BatteryPercentage *int `json:"batteryPercentage"`
|
BatteryPercentage *int `json:"batteryPercentage"`
|
||||||
|
|||||||
@@ -3,5 +3,4 @@ module git.aiterp.net/lucifer3/server
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require github.com/lucasb-eyer/go-colorful v1.2.0
|
require github.com/lucasb-eyer/go-colorful v1.2.0
|
||||||
|
require github.com/gobwas/glob v0.2.3
|
||||||
require github.com/gobwas/glob v0.2.3 // indirect
|
|
||||||
|
|||||||
@@ -180,6 +180,10 @@ func (col *Color) ToXY() (col2 Color, ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (col *Color) Interpolate(other Color, fac float64) Color {
|
func (col *Color) Interpolate(other Color, fac float64) Color {
|
||||||
|
if col.AlmostEquals(other) {
|
||||||
|
return *col
|
||||||
|
}
|
||||||
|
|
||||||
// Special case for kelvin values.
|
// Special case for kelvin values.
|
||||||
if col.IsKelvin() && other.IsKelvin() {
|
if col.IsKelvin() && other.IsKelvin() {
|
||||||
k1 := *col.K
|
k1 := *col.K
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package testutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type syncEvent struct {
|
||||||
|
id uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s syncEvent) EventDescription() string {
|
||||||
|
return fmt.Sprintf("syncEvent(%d)", s.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestEventLogger struct {
|
||||||
|
entries []any
|
||||||
|
syncN uint64
|
||||||
|
syncCh chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestEventLogger) Active() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestEventLogger) Sync(bus *lucifer3.EventBus) {
|
||||||
|
l.syncCh = make(chan struct{})
|
||||||
|
v := rand.Int63()
|
||||||
|
atomic.StoreUint64(&l.syncN, uint64(v))
|
||||||
|
|
||||||
|
bus.RunEvent(syncEvent{id: uint64(v)})
|
||||||
|
|
||||||
|
<-l.syncCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestEventLogger) AssertEvent(t *testing.T, eventStr string) {
|
||||||
|
for i, entry := range l.entries {
|
||||||
|
if event, ok := entry.(lucifer3.Event); ok && eventStr == event.EventDescription() {
|
||||||
|
l.entries = l.entries[i+1:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Errorf("Event not found: %s", eventStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestEventLogger) AssertCommand(t *testing.T, commandStr string) {
|
||||||
|
for i, entry := range l.entries {
|
||||||
|
if cmd, ok := entry.(lucifer3.Command); ok {
|
||||||
|
log.Println(cmd.CommandDescription() == commandStr)
|
||||||
|
}
|
||||||
|
if command, ok := entry.(lucifer3.Command); ok && commandStr == command.CommandDescription() {
|
||||||
|
l.entries = l.entries[i+1:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Errorf("Command not found: %s", commandStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestEventLogger) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event) {
|
||||||
|
if ev, ok := event.(syncEvent); ok {
|
||||||
|
if atomic.CompareAndSwapUint64(&l.syncN, ev.id, 0) {
|
||||||
|
close(l.syncCh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.entries = append(l.entries, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *TestEventLogger) HandleCommand(_ *lucifer3.EventBus, command lucifer3.Command) {
|
||||||
|
l.entries = append(l.entries, command)
|
||||||
|
}
|
||||||
|
|
||||||
|
func evStr(ev any) string {
|
||||||
|
if ev, ok := ev.(lucifer3.Event); ok {
|
||||||
|
return ev.EventDescription()
|
||||||
|
} else if ev, ok := ev.(lucifer3.Command); ok {
|
||||||
|
return ev.CommandDescription()
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("Unknown type")
|
||||||
|
}
|
||||||
@@ -88,10 +88,9 @@ func (s *effectEnforcer) runLoop(bus *lucifer3.EventBus) {
|
|||||||
deleteList := make([]int, 0, 8)
|
deleteList := make([]int, 0, 8)
|
||||||
batch := make(commands.SetStateBatch, 64)
|
batch := make(commands.SetStateBatch, 64)
|
||||||
|
|
||||||
for now := range time.NewTicker(time.Millisecond * 25).C {
|
for now := range time.NewTicker(time.Millisecond * 50).C {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
for i, run := range s.list {
|
for i, run := range s.list {
|
||||||
|
|
||||||
if run.dead {
|
if run.dead {
|
||||||
deleteList = append(deleteList, i-len(deleteList))
|
deleteList = append(deleteList, i-len(deleteList))
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||||
|
"git.aiterp.net/lucifer3/server/commands"
|
||||||
|
"git.aiterp.net/lucifer3/server/device"
|
||||||
|
"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/internal/testutils"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEffectEnforcer(t *testing.T) {
|
||||||
|
bus := lucifer3.EventBus{}
|
||||||
|
resolver := NewResolver()
|
||||||
|
sceneMap := NewSceneMap(resolver)
|
||||||
|
logger := &testutils.TestEventLogger{}
|
||||||
|
|
||||||
|
bus.JoinPrivileged(resolver)
|
||||||
|
bus.JoinPrivileged(sceneMap)
|
||||||
|
bus.Join(NewEffectEnforcer(resolver, sceneMap))
|
||||||
|
bus.Join(logger)
|
||||||
|
|
||||||
|
for i := 1; i <= 9; i += 1 {
|
||||||
|
bus.RunEvents([]lucifer3.Event{
|
||||||
|
events.HardwareState{
|
||||||
|
ID: "test:" + strconv.Itoa(i),
|
||||||
|
InternalName: "Test Device " + strconv.Itoa(i),
|
||||||
|
SupportFlags: device.SFlagColor | device.SFlagIntensity | device.SFlagPower,
|
||||||
|
ColorFlags: device.CFlagXY | device.CFlagHS | device.CFlagRGB | device.CFlagKelvin,
|
||||||
|
ColorGamut: nil,
|
||||||
|
ColorKelvinRange: nil,
|
||||||
|
Buttons: nil,
|
||||||
|
State: device.State{
|
||||||
|
Power: gentools.Ptr(false),
|
||||||
|
Intensity: gentools.Ptr(1.0),
|
||||||
|
Color: &color.Color{K: gentools.Ptr(2900)},
|
||||||
|
},
|
||||||
|
BatteryPercentage: nil,
|
||||||
|
Unreachable: false,
|
||||||
|
},
|
||||||
|
events.DeviceReady{ID: "test:" + strconv.Itoa(i)},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.RunCommand(commands.Assign{
|
||||||
|
Match: "test:*",
|
||||||
|
Effect: effects.Pattern{
|
||||||
|
States: []device.State{
|
||||||
|
{Color: &color.Color{XY: &color.XY{X: 0.22, Y: 0.18}}},
|
||||||
|
{Color: &color.Color{XY: &color.XY{X: 0.27, Y: 0.23}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
logger.Sync(&bus)
|
||||||
|
logger.AssertEvent(t, "DeviceReady(id:test:1)")
|
||||||
|
logger.AssertEvent(t, "DeviceReady(id:test:9)")
|
||||||
|
logger.AssertCommand(t, "Assign(test:*, Pattern(states:[(xy:0.2200,0.1800), (xy:0.2700,0.2300)], anim:0ms))")
|
||||||
|
logger.AssertCommand(t, "SetStateBatch(9 devices)")
|
||||||
|
}
|
||||||
@@ -93,6 +93,8 @@ func (b *bridge) Refresh(ctx context.Context) ([]string, error) {
|
|||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
PanelLoop:
|
PanelLoop:
|
||||||
for _, panelInfo := range overview.PanelLayout.Data.PositionData {
|
for _, panelInfo := range overview.PanelLayout.Data.PositionData {
|
||||||
|
log.Printf("%#+v", panelInfo)
|
||||||
|
|
||||||
if shapeTypeMap[panelInfo.ShapeType] == "Shapes Controller" {
|
if shapeTypeMap[panelInfo.ShapeType] == "Shapes Controller" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -290,7 +292,7 @@ func (b *bridge) Run(ctx context.Context, bus *lucifer3.EventBus) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
panelUpdate.Add(panel.message())
|
panelUpdate.Add(panel.message())
|
||||||
if panelUpdate.Len() > 150 {
|
if panelUpdate.Len() > 1256 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,8 +351,7 @@ func (b *bridge) updateEffect(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *bridge) runTouchListener(ctx context.Context, bus *lucifer3.EventBus) {
|
func (b *bridge) runTouchListener(ctx context.Context, bus *lucifer3.EventBus) {
|
||||||
cooldownID := ""
|
cooldownMap := make(map[uint16]time.Time)
|
||||||
cooldownUntil := time.Now()
|
|
||||||
|
|
||||||
message := make(PanelEventMessage, 65536)
|
message := make(PanelEventMessage, 65536)
|
||||||
reqCloser := io.Closer(nil)
|
reqCloser := io.Closer(nil)
|
||||||
@@ -408,23 +409,29 @@ func (b *bridge) runTouchListener(ctx context.Context, bus *lucifer3.EventBus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < message.Count(); i += 1 {
|
for i := 0; i < message.Count(); i += 1 {
|
||||||
|
panelID := message.PanelID(i)
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
id, hasID := b.panelIDMap[message.PanelID(i)]
|
id, hasID := b.panelIDMap[panelID]
|
||||||
swipedFromID := b.panelIDMap[message.SwipedFromPanelID(i)]
|
swipedFromID := b.panelIDMap[message.SwipedFromPanelID(i)]
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
|
|
||||||
if !hasID || (id == cooldownID && time.Now().Before(cooldownUntil)) {
|
if time.Now().Before(cooldownMap[panelID]) {
|
||||||
continue
|
continue
|
||||||
}
|
} else if !hasID {
|
||||||
|
bus.RunEvent(events.ButtonPressed{
|
||||||
|
ID: fmt.Sprintf("nanoleaf:%s:meta:%x", b.host, panelID),
|
||||||
|
SwipedID: swipedFromID,
|
||||||
|
Name: "Touch",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
bus.RunEvent(events.ButtonPressed{
|
bus.RunEvent(events.ButtonPressed{
|
||||||
ID: id,
|
ID: id,
|
||||||
SwipedID: swipedFromID,
|
SwipedID: swipedFromID,
|
||||||
Name: "Touch",
|
Name: "Touch",
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
cooldownID = id
|
cooldownMap[panelID] = time.Now().Add(time.Second)
|
||||||
cooldownUntil = time.Now().Add(time.Second)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
lucifer3 "git.aiterp.net/lucifer3/server"
|
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||||
"git.aiterp.net/lucifer3/server/commands"
|
"git.aiterp.net/lucifer3/server/commands"
|
||||||
"git.aiterp.net/lucifer3/server/device"
|
"git.aiterp.net/lucifer3/server/device"
|
||||||
"log"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -58,7 +57,6 @@ func (s *SceneMap) HandleCommand(_ *lucifer3.EventBus, command lucifer3.Command)
|
|||||||
if len(matched) > 0 {
|
if len(matched) > 0 {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
for _, ptr := range matched {
|
for _, ptr := range matched {
|
||||||
log.Println("Got")
|
|
||||||
delete(s.sceneMap, ptr.ID)
|
delete(s.sceneMap, ptr.ID)
|
||||||
}
|
}
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|||||||
Reference in New Issue
Block a user