You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
6.2 KiB
271 lines
6.2 KiB
package api
|
|
|
|
import (
|
|
"context"
|
|
"git.aiterp.net/lucifer/new-server/app/config"
|
|
"git.aiterp.net/lucifer/new-server/app/services/scene"
|
|
"git.aiterp.net/lucifer/new-server/models"
|
|
"github.com/gin-gonic/gin"
|
|
"golang.org/x/sync/errgroup"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func fetchDevices(ctx context.Context, fetchStr string) ([]models.Device, error) {
|
|
if strings.HasPrefix(fetchStr, "tag:") {
|
|
return config.DeviceRepository().FetchByReference(ctx, models.RKTag, fetchStr[4:])
|
|
} else if strings.HasPrefix(fetchStr, "bridge:") {
|
|
return config.DeviceRepository().FetchByReference(ctx, models.RKBridgeID, fetchStr[7:])
|
|
} else if strings.HasPrefix(fetchStr, "id:") {
|
|
return config.DeviceRepository().FetchByReference(ctx, models.RKDeviceID, fetchStr[7:])
|
|
} else if strings.HasPrefix(fetchStr, "name:") {
|
|
return config.DeviceRepository().FetchByReference(ctx, models.RKName, fetchStr[7:])
|
|
}else if fetchStr == "all" {
|
|
return config.DeviceRepository().FetchByReference(ctx, models.RKAll, "")
|
|
} else {
|
|
_, err := strconv.Atoi(fetchStr)
|
|
if err != nil {
|
|
return config.DeviceRepository().FetchByReference(ctx, models.RKName, fetchStr)
|
|
}
|
|
|
|
return config.DeviceRepository().FetchByReference(ctx, models.RKDeviceID, fetchStr)
|
|
}
|
|
}
|
|
|
|
func Devices(r gin.IRoutes) {
|
|
r.GET("", handler(func(c *gin.Context) (interface{}, error) {
|
|
return config.DeviceRepository().FetchByReference(ctxOf(c), models.RKAll, "")
|
|
}))
|
|
|
|
r.GET("/:fetch", handler(func(c *gin.Context) (interface{}, error) {
|
|
return fetchDevices(ctxOf(c), c.Param("fetch"))
|
|
}))
|
|
|
|
r.PUT("", handler(func(c *gin.Context) (interface{}, error) {
|
|
var body []struct {
|
|
Fetch string `json:"fetch"`
|
|
SetState models.NewDeviceState `json:"setState"`
|
|
}
|
|
err := parseBody(c, &body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
set := make(map[int]bool)
|
|
changed := make([]models.Device, 0, 64)
|
|
for _, job := range body {
|
|
devices, err := fetchDevices(ctxOf(c), job.Fetch)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(devices) == 0 {
|
|
return []models.Device{}, nil
|
|
}
|
|
|
|
for i := range devices {
|
|
if set[devices[i].ID] {
|
|
continue
|
|
}
|
|
|
|
err := devices[i].SetState(job.SetState)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_ = scene.GlobalManager().UpdateDevice(ctxOf(c), &devices[i], nil)
|
|
|
|
set[devices[i].ID] = true
|
|
changed = append(changed, devices[i])
|
|
}
|
|
}
|
|
|
|
|
|
config.PublishChannel <- scene.GlobalManager().FilterUnassigned(changed)
|
|
|
|
go func() {
|
|
for _, device := range changed {
|
|
err := config.DeviceRepository().Save(context.Background(), &device, models.SMState)
|
|
if err != nil {
|
|
log.Println("Failed to save device for state:", err)
|
|
continue
|
|
}
|
|
}
|
|
}()
|
|
|
|
return changed, nil
|
|
}))
|
|
|
|
r.PUT("/:fetch", handler(func(c *gin.Context) (interface{}, error) {
|
|
update := models.DeviceUpdate{}
|
|
err := parseBody(c, &update)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(devices) == 0 {
|
|
return []models.Device{}, nil
|
|
}
|
|
|
|
for i := range devices {
|
|
devices[i].ApplyUpdate(update)
|
|
|
|
err := config.DeviceRepository().Save(context.Background(), &devices[i], models.SMProperties)
|
|
if err != nil {
|
|
log.Println("Failed to save device for state:", err)
|
|
continue
|
|
}
|
|
}
|
|
|
|
return devices, nil
|
|
}))
|
|
|
|
r.PUT("/:fetch/state", handler(func(c *gin.Context) (interface{}, error) {
|
|
state := models.NewDeviceState{}
|
|
err := parseBody(c, &state)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(devices) == 0 {
|
|
return []models.Device{}, nil
|
|
}
|
|
|
|
for i := range devices {
|
|
err := devices[i].SetState(state)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_ = scene.GlobalManager().UpdateDevice(ctxOf(c), &devices[i], nil)
|
|
}
|
|
|
|
config.PublishChannel <- scene.GlobalManager().FilterUnassigned(devices)
|
|
|
|
go func() {
|
|
for _, device := range devices {
|
|
err := config.DeviceRepository().Save(context.Background(), &device, models.SMState)
|
|
if err != nil {
|
|
log.Println("Failed to save device for state:", err)
|
|
continue
|
|
}
|
|
}
|
|
}()
|
|
|
|
return devices, nil
|
|
}))
|
|
|
|
r.PUT("/:fetch/tags", handler(func(c *gin.Context) (interface{}, error) {
|
|
var body struct {
|
|
Add []string `json:"add"`
|
|
Remove []string `json:"remove"`
|
|
}
|
|
err := parseBody(c, &body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(devices) == 0 {
|
|
return []models.Device{}, nil
|
|
}
|
|
|
|
for i := range devices {
|
|
device := &devices[i]
|
|
|
|
for _, tag := range body.Add {
|
|
found := false
|
|
for _, tag2 := range device.Tags {
|
|
if tag == tag2 {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
device.Tags = append(device.Tags, tag)
|
|
}
|
|
}
|
|
for _, tag := range body.Remove {
|
|
index := -1
|
|
for i, tag2 := range device.Tags {
|
|
if tag == tag2 {
|
|
index = i
|
|
}
|
|
}
|
|
if index == -1 {
|
|
continue
|
|
}
|
|
|
|
device.Tags = append(device.Tags[:index], device.Tags[index+1:]...)
|
|
}
|
|
|
|
err = config.DeviceRepository().Save(ctxOf(c), device, models.SMTags)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return devices, nil
|
|
}))
|
|
|
|
r.PUT("/:fetch/scene", handler(func(c *gin.Context) (interface{}, error) {
|
|
var body models.DeviceSceneAssignment
|
|
err := parseBody(c, &body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(devices) == 0 {
|
|
return []models.Device{}, nil
|
|
}
|
|
|
|
assignedScene, err := config.SceneRepository().Find(ctxOf(c), body.SceneID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if body.DurationMS < 0 {
|
|
body.DurationMS = 0
|
|
}
|
|
body.StartTime = time.Now()
|
|
|
|
for i := range devices {
|
|
devices[i].SceneAssignments = []models.DeviceSceneAssignment{body}
|
|
|
|
err := scene.GlobalManager().UpdateDevice(ctxOf(c), &devices[i], assignedScene)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
eg := errgroup.Group{}
|
|
for i := range devices {
|
|
eg.Go(func() error {
|
|
return config.DeviceRepository().Save(ctxOf(c), &devices[i], 0)
|
|
})
|
|
}
|
|
|
|
err = eg.Wait()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return devices, nil
|
|
}))
|
|
}
|