Browse Source

add resolve service prototype.

beelzebub
Gisle Aune 2 years ago
parent
commit
4142645115
  1. 32
      cmd/bustest/main.go
  2. 33
      commands/edit.go
  3. 4
      device/flags.go
  4. 18
      device/state.go
  5. 8
      events/device.go
  6. 24
      internal/gentools/slices.go
  7. 2
      service.go
  8. 3
      services/package.go
  9. 149
      services/resolver.go

32
cmd/bustest/main.go

@ -1,35 +1,30 @@
package main package main
import ( import (
"fmt"
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"
"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"
"log"
"git.aiterp.net/lucifer3/server/services"
"time" "time"
) )
func main() { func main() {
bus := lucifer3.EventBus{} bus := lucifer3.EventBus{}
bus.JoinCallback(func(event lucifer3.Event) bool {
switch event := event.(type) {
case events.Connected:
log.Println("Callback got connect event for", event.Prefix)
}
return true
})
bus.Join(services.Resolver())
bus.RunEvent(events.Connected{Prefix: "nanoleaf:10.80.1.11"}) bus.RunEvent(events.Connected{Prefix: "nanoleaf:10.80.1.11"})
time.Sleep(time.Second / 2) time.Sleep(time.Second / 2)
for _, id := range []string{"e28c", "67db", "f744", "d057", "73c1"} {
for i, id := range []string{"e28c", "67db", "f744", "d057", "73c1"} {
bus.RunEvent(events.HardwareState{ bus.RunEvent(events.HardwareState{
DeviceID: "nanoleaf:10.80.1.11:" + id,
ID: "nanoleaf:10.80.1.11:" + id,
InternalName: fmt.Sprintf("Hexagon %d", i+1),
SupportFlags: device.SFlagPower | device.SFlagColor | device.SFlagIntensity, SupportFlags: device.SFlagPower | device.SFlagColor | device.SFlagIntensity,
ColorFlags: device.CFlagRGB, ColorFlags: device.CFlagRGB,
State: device.State{}, State: device.State{},
@ -37,9 +32,12 @@ func main() {
} }
bus.RunCommand(commands.ReplaceScene{ bus.RunCommand(commands.ReplaceScene{
IDs: []string{"nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"},
IDs: []string{"lucifer:name:Hexagon*"},
SceneID: 7, SceneID: 7,
}) })
time.Sleep(time.Second / 4)
for _, id := range []string{"nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"} { for _, id := range []string{"nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"} {
bus.RunCommand(commands.SetState{ bus.RunCommand(commands.SetState{
ID: id, ID: id,
@ -54,15 +52,19 @@ func main() {
time.Sleep(time.Second / 2) time.Sleep(time.Second / 2)
bus.RunEvent(events.HardwareState{ bus.RunEvent(events.HardwareState{
DeviceID: "nanoleaf:10.80.1.11:40e5",
ID: "nanoleaf:10.80.1.11:40e5",
InternalName: "Hexagon 6",
SupportFlags: device.SFlagPower | device.SFlagColor | device.SFlagIntensity, SupportFlags: device.SFlagPower | device.SFlagColor | device.SFlagIntensity,
ColorFlags: device.CFlagRGB, ColorFlags: device.CFlagRGB,
State: device.State{}, State: device.State{},
}) })
bus.RunCommand(commands.ReplaceScene{ bus.RunCommand(commands.ReplaceScene{
IDs: []string{"nanoleaf:10.80.1.11:40e5", "nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"},
IDs: []string{"lucifer:name:Hexagon*"},
SceneID: 7, SceneID: 7,
}) })
time.Sleep(time.Second / 4)
bus.RunCommand(commands.SetState{ bus.RunCommand(commands.SetState{
ID: "nanoleaf:10.80.1.11:40e5", ID: "nanoleaf:10.80.1.11:40e5",
State: device.State{ State: device.State{
@ -73,6 +75,4 @@ func main() {
}) })
time.Sleep(time.Second / 4) time.Sleep(time.Second / 4)
time.Sleep(time.Second * 4)
} }

33
commands/edit.go

@ -0,0 +1,33 @@
package commands
import (
"fmt"
"git.aiterp.net/lucifer3/server/internal/formattools"
)
type SetName struct {
ID string
Name string
}
func (c SetName) CommandDescription() string {
return fmt.Sprintf("SetName(%v, %s)", c.ID, c.Name)
}
type AddTag struct {
IDs []string
Tag string
}
func (c AddTag) CommandDescription() string {
return fmt.Sprintf("AddTag(%v, %s)", formattools.CompactIDList(c.IDs), c.Tag)
}
type RemoveTag struct {
IDs []string
Tag string
}
func (c RemoveTag) CommandDescription() string {
return fmt.Sprintf("RemoveTag(%v, %s)", formattools.CompactIDList(c.IDs), c.Tag)
}

4
device/flags.go

@ -58,11 +58,11 @@ func (f ColorFlag) IsWarmWhite() bool {
} }
func (f ColorFlag) IsColorOnly() bool { func (f ColorFlag) IsColorOnly() bool {
return f != 0 && f&CFlagKelvin == 0
return f.IsColor() && !f.IsWarmWhite()
} }
func (f ColorFlag) IsWarmWhiteOnly() bool { func (f ColorFlag) IsWarmWhiteOnly() bool {
return f == CFlagKelvin
return f.IsWarmWhite() && !f.IsColor()
} }
func (f ColorFlag) HasAny(d ColorFlag) bool { func (f ColorFlag) HasAny(d ColorFlag) bool {

18
device/state.go

@ -11,11 +11,6 @@ type State struct {
Temperature *float64 `json:"temperature"` Temperature *float64 `json:"temperature"`
Intensity *float64 `json:"intensity"` Intensity *float64 `json:"intensity"`
Color *color.Color `json:"color"` Color *color.Color `json:"color"`
SensedTemperature *float64 `json:"sensedTemperature"`
SensedLightLevel *float64 `json:"sensedLightLevel"`
SensedPresence *PresenceState `json:"sensedPresence"`
SensedButton *int `json:"sensedButton"`
} }
func (d State) String() string { func (d State) String() string {
@ -33,19 +28,6 @@ func (d State) String() string {
parts = append(parts, fmt.Sprintf("color:%s", d.Color.String())) parts = append(parts, fmt.Sprintf("color:%s", d.Color.String()))
} }
if d.SensedTemperature != nil {
parts = append(parts, fmt.Sprintf("sensedTemperature:%.2f", *d.SensedTemperature))
}
if d.SensedLightLevel != nil {
parts = append(parts, fmt.Sprintf("sensedLightLevel:%.1f", *d.SensedLightLevel))
}
if d.SensedPresence != nil {
parts = append(parts, fmt.Sprintf("sensedPresense:(%t,%.1f)", d.SensedPresence.Present, d.SensedPresence.AbsenceSeconds))
}
if d.SensedButton != nil {
parts = append(parts, fmt.Sprintf("sensedButton:%d", *d.SensedButton))
}
return fmt.Sprint("(", strings.Join(parts, ", "), ")") return fmt.Sprint("(", strings.Join(parts, ", "), ")")
} }

8
events/device.go

@ -6,15 +6,17 @@ import (
) )
type HardwareState struct { type HardwareState struct {
DeviceID string `json:"internalId"`
ID string `json:"internalId"`
InternalName string `json:"internalName"`
SupportFlags device.SupportFlags `json:"deviceFlags"` SupportFlags device.SupportFlags `json:"deviceFlags"`
ColorFlags device.ColorFlag `json:"colorFlags"` ColorFlags device.ColorFlag `json:"colorFlags"`
Buttons []string `json:"buttons"`
State device.State `json:"state"` State device.State `json:"state"`
} }
func (d HardwareState) EventDescription() string { func (d HardwareState) EventDescription() string {
return fmt.Sprintf("HardwareState(id:%s, sflags:%s, cflags:%s, state:%s)",
d.DeviceID, d.SupportFlags, d.ColorFlags, d.State,
return fmt.Sprintf("HardwareState(id:%s, iname:%#+v, sflags:%s, cflags:%s, buttons:%v, state:%s)",
d.ID, d.InternalName, d.SupportFlags, d.ColorFlags, d.Buttons, d.State,
) )
} }

24
internal/gentools/slices.go

@ -0,0 +1,24 @@
package gentools
func IndexOf[T comparable](arr []T, value T) int {
for i, v := range arr {
if v == value {
return i
}
}
return -1
}
func AddUniques[T comparable](arr *[]T, values ...T) {
Outer:
for _, v := range values {
for _, v2 := range *arr {
if v2 == v {
continue Outer
}
}
*arr = append(*arr, v)
}
}

2
service.go

@ -10,6 +10,8 @@ type Service interface {
} }
type ActiveService interface { type ActiveService interface {
Service
HandleCommand(bus *EventBus, command Command) HandleCommand(bus *EventBus, command Command)
} }

3
services/package.go

@ -0,0 +1,3 @@
// Package services is for self-contained general services. Things like bridges, databases and such should
// not be in here.
package services

149
services/resolver.go

@ -0,0 +1,149 @@
package services
import (
lucifer3 "git.aiterp.net/lucifer3/server"
"git.aiterp.net/lucifer3/server/commands"
"git.aiterp.net/lucifer3/server/events"
"git.aiterp.net/lucifer3/server/internal/gentools"
"strings"
)
func Resolver() lucifer3.Service {
return &resolver{
tags: make(map[string][]string),
names: make(map[string][]string),
}
}
type resolver struct {
tags map[string][]string
names map[string][]string
}
func (r *resolver) Active() bool {
return true
}
func (r *resolver) HandleEvent(_ *lucifer3.EventBus, event lucifer3.Event) {
switch event := event.(type) {
case events.HardwareState:
// On HardwareState, use the internal name if (and only if) it is not already named.
if event.InternalName != "" {
found := false
for _, ids := range r.names {
i := gentools.IndexOf(ids, event.ID)
if i != -1 {
found = true
break
}
}
if !found {
r.names[event.InternalName] = append(r.names[event.InternalName], event.ID)
}
}
}
}
func (r *resolver) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command) {
var resolveList []string
var resolveCB func(newIDs []string) lucifer3.Command
switch command := command.(type) {
case commands.Assign:
resolveList = command.IDs
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
case commands.ReplaceScene:
resolveList = command.IDs
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
case commands.ClearScene:
resolveList = command.IDs
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
case commands.SetName:
if !strings.HasPrefix(command.ID, "lucifer:") {
for name := range r.names {
i := gentools.IndexOf(r.names[name], command.ID)
if i != -1 {
r.names[name] = append(r.names[name][:i], r.names[name][i+1:]...)
}
}
r.names[command.Name] = append(r.names[command.Name], command.ID)
}
case commands.AddTag:
for _, id := range command.IDs {
if strings.HasPrefix(id, "lucifer:") {
continue
}
found := false
for _, id2 := range r.tags[command.Tag] {
if id == id2 {
found = true
break
}
}
if !found {
r.tags[command.Tag] = append(r.tags[command.Tag], id)
}
}
resolveList = command.IDs
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
case commands.RemoveTag:
for _, id := range command.IDs {
if strings.HasPrefix(id, "lucifer:") {
continue
}
for i, id2 := range r.tags[command.Tag] {
if id == id2 {
r.tags[command.Tag] = append(r.tags[command.Tag][:i], r.tags[command.Tag][i+1:]...)
break
}
}
}
resolveList = command.IDs
resolveCB = func(newIDs []string) lucifer3.Command { command.IDs = newIDs; return command }
}
if resolveList != nil && resolveCB != nil {
newList := make([]string, 0, 16)
for _, id := range resolveList {
switch {
case strings.HasPrefix(id, "lucifer:tag:"):
tag := id[12:]
if len(newList) == 0 {
newList = append(newList, r.tags[tag]...)
} else {
gentools.AddUniques(&newList, r.tags[tag]...)
}
case strings.HasPrefix(id, "lucifer:name:"):
name := id[13:]
if strings.HasSuffix(name, "*") {
prefix := name[:len(name)-1]
for name, ids := range r.names {
if strings.HasPrefix(name, prefix) {
gentools.AddUniques(&newList, ids...)
}
}
} else if strings.HasPrefix(name, "*") {
suffix := name[1:]
for name, ids := range r.names {
if strings.HasSuffix(name, suffix) {
gentools.AddUniques(&newList, ids...)
}
}
} else {
gentools.AddUniques(&newList, r.names[name]...)
}
}
}
if len(newList) > 0 {
bus.RunCommand(resolveCB(newList))
}
}
}
Loading…
Cancel
Save