websocket backend + frontend patching.
This commit is contained in:
@@ -8,10 +8,12 @@ import (
|
||||
"git.aiterp.net/lucifer3/server/services/script"
|
||||
"git.aiterp.net/lucifer3/server/services/uistate"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -24,6 +26,11 @@ var zeroUUID = uuid.UUID{
|
||||
|
||||
func New(addr string) (lucifer3.Service, error) {
|
||||
svc := &service{}
|
||||
upgrader := websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
e := echo.New()
|
||||
e.HideBanner = true
|
||||
@@ -38,6 +45,28 @@ func New(addr string) (lucifer3.Service, error) {
|
||||
return c.JSON(200, data)
|
||||
})
|
||||
|
||||
e.GET("/subscribe", func(c echo.Context) error {
|
||||
ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sub := make(chan uistate.Patch, 64)
|
||||
svc.addSub(sub)
|
||||
|
||||
defer svc.removeSub(sub)
|
||||
defer ws.Close()
|
||||
|
||||
for patch := range sub {
|
||||
err := ws.WriteJSON(patch)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
e.POST("/command", func(c echo.Context) error {
|
||||
var input commandInput
|
||||
err := c.Bind(&input)
|
||||
@@ -115,6 +144,7 @@ type service struct {
|
||||
mu sync.Mutex
|
||||
data uistate.Data
|
||||
bus *lucifer3.EventBus
|
||||
subs []chan uistate.Patch
|
||||
}
|
||||
|
||||
func (s *service) Active() bool {
|
||||
@@ -130,12 +160,36 @@ func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
|
||||
case uistate.Patch:
|
||||
s.mu.Lock()
|
||||
s.data = s.data.WithPatch(event)
|
||||
subs := s.subs
|
||||
s.mu.Unlock()
|
||||
|
||||
// TODO: Broadcast websockets
|
||||
for _, sub := range subs {
|
||||
select {
|
||||
case sub <- event:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *service) addSub(ch chan uistate.Patch) {
|
||||
s.mu.Lock()
|
||||
s.subs = append(s.subs, ch)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *service) removeSub(ch chan uistate.Patch) {
|
||||
s.mu.Lock()
|
||||
s.subs = append([]chan uistate.Patch{}, s.subs...)
|
||||
for i, sub := range s.subs {
|
||||
if sub == ch {
|
||||
s.subs = append(s.subs[:i], s.subs[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
type commandInput struct {
|
||||
Assign *assignInput `json:"assign,omitempty"`
|
||||
AddAlias *commands.AddAlias `json:"addAlias,omitempty"`
|
||||
|
||||
@@ -155,7 +155,7 @@ func (b *Bridge) RefreshAll() ([]lucifer3.Event, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if res.Type == "device" {
|
||||
if res.Type == "device" && !strings.HasPrefix(res.Metadata.Archetype, "bridge") {
|
||||
hwState, hwEvent := res.GenerateEvent(b.host, resources)
|
||||
if hwState.SupportFlags == 0 {
|
||||
continue
|
||||
@@ -219,7 +219,7 @@ func (b *Bridge) ApplyPatches(date time.Time, resources []ResourceData) (eventLi
|
||||
|
||||
for _, resource := range resources {
|
||||
if resource.Owner != nil && resource.Owner.Kind == "device" {
|
||||
if parent, ok := mapCopy[resource.Owner.ID]; ok {
|
||||
if parent, ok := mapCopy[resource.Owner.ID]; ok && !strings.HasPrefix(parent.Metadata.Archetype, "bridge") {
|
||||
hwState, _ := parent.GenerateEvent(b.host, mapCopy)
|
||||
eventList = append(eventList, hwState)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ func (d *Data) WithPatch(patches ...Patch) Data {
|
||||
gentools.ApplyUpdatePtr(&pd.DesiredState, patch.Device.DesiredState)
|
||||
gentools.ApplyUpdatePtr(&pd.Assignment, patch.Device.Assignment)
|
||||
gentools.ApplyUpdatePtr(&pd.ActiveColorRGB, patch.Device.ActiveColorRGB)
|
||||
gentools.ApplyUpdatePtr(&pd.DesiredColorRGB, patch.Device.DesiredColorRGB)
|
||||
|
||||
if patch.Device.AddAlias != nil {
|
||||
pd.Aliases = append(pd.Aliases[:0:0], pd.Aliases...)
|
||||
@@ -125,15 +126,16 @@ func (d *Data) ensureAssignment(id uuid.UUID) Assignment {
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
HWMetadata *events.HardwareMetadata `json:"hwMetadata"`
|
||||
HWState *events.HardwareState `json:"hwState"`
|
||||
ActiveColorRGB *color.RGB `json:"activeColorRgb"`
|
||||
DesiredState *device.State `json:"desiredState"`
|
||||
Aliases []string `json:"aliases"`
|
||||
Assignment *uuid.UUID `json:"assignment"`
|
||||
Sensors DeviceSensors `json:"sensors"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
HWMetadata *events.HardwareMetadata `json:"hwMetadata"`
|
||||
HWState *events.HardwareState `json:"hwState"`
|
||||
DesiredColorRGB *color.RGB `json:"desiredColorRgb"`
|
||||
ActiveColorRGB *color.RGB `json:"activeColorRgb"`
|
||||
DesiredState *device.State `json:"desiredState"`
|
||||
Aliases []string `json:"aliases"`
|
||||
Assignment *uuid.UUID `json:"assignment"`
|
||||
Sensors DeviceSensors `json:"sensors"`
|
||||
}
|
||||
|
||||
type DeviceSensors struct {
|
||||
|
||||
@@ -38,6 +38,9 @@ func (e Patch) EventDescription() string {
|
||||
case e.Device.ActiveColorRGB != nil:
|
||||
col := color.Color{RGB: e.Device.ActiveColorRGB}
|
||||
return fmt.Sprintf("uistate.Patch(device=%s, activeColorRgb=%s)", e.Device.ID, col.String())
|
||||
case e.Device.DesiredColorRGB != nil:
|
||||
col := color.Color{RGB: e.Device.DesiredColorRGB}
|
||||
return fmt.Sprintf("uistate.Patch(device=%s, desiredColorRgb=%s)", e.Device.ID, col.String())
|
||||
default:
|
||||
return fmt.Sprintf("uistate.Patch(device=%s, ...other)", e.Device.ID)
|
||||
}
|
||||
@@ -61,6 +64,7 @@ type DevicePatch struct {
|
||||
RemoveAlias *string `json:"removeAlias,omitempty"`
|
||||
Assignment *uuid.UUID `json:"assignment,omitempty"`
|
||||
ClearAssignment bool `json:"clearAssignment,omitempty"`
|
||||
DesiredColorRGB *color.RGB `json:"desiredColorRgb,omitempty"`
|
||||
ActiveColorRGB *color.RGB `json:"activeColorRgb,omitempty"`
|
||||
ClearActiveColorRGB bool `json:"clearActiveColorRGB,omitempty"`
|
||||
Sensors *DeviceSensors `json:"sensors,omitempty"`
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"git.aiterp.net/lucifer3/server/commands"
|
||||
"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/services/script"
|
||||
"github.com/google/uuid"
|
||||
@@ -36,11 +37,29 @@ func (s *service) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Command
|
||||
|
||||
switch command := command.(type) {
|
||||
case commands.SetState:
|
||||
patches = []Patch{{Device: &DevicePatch{ID: command.ID, DesiredState: &command.State}}}
|
||||
rgb := color.Color{}
|
||||
if command.State.Color != nil {
|
||||
rgb, _ = command.State.Color.ToRGB()
|
||||
}
|
||||
|
||||
patches = []Patch{{Device: &DevicePatch{
|
||||
ID: command.ID,
|
||||
DesiredState: &command.State,
|
||||
DesiredColorRGB: rgb.RGB,
|
||||
}}}
|
||||
case commands.SetStateBatch:
|
||||
for id, state := range command {
|
||||
rgb := color.Color{}
|
||||
if state.Color != nil {
|
||||
rgb, _ = state.Color.ToRGB()
|
||||
}
|
||||
|
||||
patches = append(patches, Patch{
|
||||
Device: &DevicePatch{ID: id, DesiredState: gentools.ShallowCopy(&state)},
|
||||
Device: &DevicePatch{
|
||||
ID: id,
|
||||
DesiredState: gentools.ShallowCopy(&state),
|
||||
DesiredColorRGB: rgb.RGB,
|
||||
},
|
||||
})
|
||||
}
|
||||
case script.Update:
|
||||
@@ -129,10 +148,13 @@ func (s *service) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
|
||||
}})
|
||||
case events.AssignmentVariables:
|
||||
// Always set the assignment
|
||||
patches = append(patches, Patch{Assignment: &AssignmentPatch{
|
||||
ID: event.ID,
|
||||
Variables: event.Map,
|
||||
}})
|
||||
if len(event.Map) > 0 {
|
||||
patches = append(patches, Patch{Assignment: &AssignmentPatch{
|
||||
ID: event.ID,
|
||||
Variables: event.Map,
|
||||
}})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(patches) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user