This commit is contained in:
+60
-25
@@ -3,9 +3,9 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"git.aiterp.net/lucifer/new-server/app/config"
|
||||
"git.aiterp.net/lucifer/new-server/app/services/publisher"
|
||||
"git.aiterp.net/lucifer/new-server/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
@@ -17,11 +17,21 @@ func fetchDevices(ctx context.Context, fetchStr string) ([]models.Device, error)
|
||||
|
||||
func Devices(r gin.IRoutes) {
|
||||
r.GET("", handler(func(c *gin.Context) (interface{}, error) {
|
||||
return config.DeviceRepository().FetchByReference(ctxOf(c), models.RKAll, "")
|
||||
devices, err := config.DeviceRepository().FetchByReference(ctxOf(c), models.RKAll, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return withSceneState(devices), nil
|
||||
}))
|
||||
|
||||
r.GET("/:fetch", handler(func(c *gin.Context) (interface{}, error) {
|
||||
return fetchDevices(ctxOf(c), c.Param("fetch"))
|
||||
devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return withSceneState(devices), nil
|
||||
}))
|
||||
|
||||
r.PUT("", handler(func(c *gin.Context) (interface{}, error) {
|
||||
@@ -72,7 +82,7 @@ func Devices(r gin.IRoutes) {
|
||||
}
|
||||
}()
|
||||
|
||||
return changed, nil
|
||||
return withSceneState(changed), nil
|
||||
}))
|
||||
|
||||
r.PUT("/:fetch", handler(func(c *gin.Context) (interface{}, error) {
|
||||
@@ -100,7 +110,7 @@ func Devices(r gin.IRoutes) {
|
||||
}
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
return withSceneState(devices), nil
|
||||
}))
|
||||
|
||||
r.PUT("/:fetch/state", handler(func(c *gin.Context) (interface{}, error) {
|
||||
@@ -128,16 +138,13 @@ func Devices(r gin.IRoutes) {
|
||||
config.PublishChannel <- 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
|
||||
}
|
||||
err = config.DeviceRepository().SaveMany(ctxOf(c), models.SMState, devices)
|
||||
if err != nil {
|
||||
log.Println("Failed to save devices states")
|
||||
}
|
||||
}()
|
||||
|
||||
return devices, nil
|
||||
return withSceneState(devices), nil
|
||||
}))
|
||||
|
||||
r.PUT("/:fetch/tags", handler(func(c *gin.Context) (interface{}, error) {
|
||||
@@ -194,7 +201,7 @@ func Devices(r gin.IRoutes) {
|
||||
}
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
return withSceneState(devices), nil
|
||||
}))
|
||||
|
||||
r.PUT("/:fetch/scene", handler(func(c *gin.Context) (interface{}, error) {
|
||||
@@ -221,25 +228,53 @@ func Devices(r gin.IRoutes) {
|
||||
}
|
||||
body.StartTime = time.Now()
|
||||
|
||||
pushMode := c.Query("push") == "true"
|
||||
for i := range devices {
|
||||
devices[i].SceneAssignments = []models.DeviceSceneAssignment{body}
|
||||
if pushMode {
|
||||
devices[i].SceneAssignments = append(devices[i].SceneAssignments, body)
|
||||
} else {
|
||||
devices[i].SceneAssignments = []models.DeviceSceneAssignment{body}
|
||||
}
|
||||
}
|
||||
config.PublishChannel <- devices
|
||||
|
||||
eg := errgroup.Group{}
|
||||
for i := range devices {
|
||||
device := devices[i]
|
||||
|
||||
eg.Go(func() error {
|
||||
return config.DeviceRepository().Save(ctxOf(c), &device, 0)
|
||||
})
|
||||
}
|
||||
|
||||
err = eg.Wait()
|
||||
err = config.DeviceRepository().SaveMany(ctxOf(c), 0, devices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
return withSceneState(devices), nil
|
||||
}))
|
||||
|
||||
r.DELETE("/:fetch/scene", handler(func(c *gin.Context) (interface{}, error) {
|
||||
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].SceneAssignments = []models.DeviceSceneAssignment{}
|
||||
}
|
||||
config.PublishChannel <- devices
|
||||
|
||||
err = config.DeviceRepository().SaveMany(ctxOf(c), 0, devices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return withSceneState(devices), nil
|
||||
}))
|
||||
}
|
||||
|
||||
func withSceneState(devices []models.Device) []models.Device {
|
||||
res := make([]models.Device, 0, len(devices))
|
||||
for _, device := range devices {
|
||||
device.SceneState = publisher.Global().SceneState(device.ID)
|
||||
res = append(res, device)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
+17
-2
@@ -60,9 +60,24 @@ func (client *Client) PutDeviceTags(ctx context.Context, fetchStr string, addTag
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func (client *Client) AssignDevice(ctx context.Context, fetchStr string, assignment models.DeviceSceneAssignment) ([]models.Device, error) {
|
||||
func (client *Client) AssignDevice(ctx context.Context, fetchStr string, push bool, assignment models.DeviceSceneAssignment) ([]models.Device, error) {
|
||||
query := ""
|
||||
if push {
|
||||
query = "?push=true"
|
||||
}
|
||||
|
||||
devices := make([]models.Device, 0, 16)
|
||||
err := client.Fetch(ctx, "PUT", "/api/devices/"+fetchStr+"/scene", &devices, assignment)
|
||||
err := client.Fetch(ctx, "PUT", "/api/devices/"+fetchStr+"/scene"+query, &devices, assignment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func (client *Client) ClearDevice(ctx context.Context, fetchStr string) ([]models.Device, error) {
|
||||
devices := make([]models.Device, 0, 16)
|
||||
err := client.Fetch(ctx, "DELETE", "/api/devices/"+fetchStr+"/scene", &devices, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func (client *Client) GetHandler(ctx context.Context, id int) (*models.EventHand
|
||||
|
||||
func (client *Client) PutHandler(ctx context.Context, handler *models.EventHandler) (*models.EventHandler, error) {
|
||||
var response models.EventHandler
|
||||
err := client.Fetch(ctx, "PUT", fmt.Sprintf("/api/event-handlers/%d", handler.ID), &response, handler)
|
||||
err := client.Fetch(ctx, "PUT", fmt.Sprintf("/api/event-handlers/%d?hard=true", handler.ID), &response, handler)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"git.aiterp.net/lucifer/new-server/app/config"
|
||||
"git.aiterp.net/lucifer/new-server/models"
|
||||
"log"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -53,15 +52,6 @@ func runConnectToBridges() error {
|
||||
log.Printf("Running bridge \"%s\" (%d)", bridge.Name, bridge.ID)
|
||||
|
||||
go func(bridge models.Bridge, cancel func()) {
|
||||
savedDevices, err := config.DeviceRepository().FetchByReference(ctx, models.RKBridgeID, strconv.Itoa(bridge.ID))
|
||||
if err != nil {
|
||||
log.Println("Failed to fetch devices from db for refresh:", err)
|
||||
}
|
||||
err = driver.Publish(ctx, bridge, savedDevices)
|
||||
if err != nil {
|
||||
log.Println("Failed to publish devices from db before run:", err)
|
||||
}
|
||||
|
||||
err = driver.Run(ctx, bridge, config.EventChannel)
|
||||
log.Printf("Bridge \"%s\" (%d) stopped: %s", bridge.Name, bridge.ID, err)
|
||||
|
||||
|
||||
@@ -155,6 +155,15 @@ func handleEvent(event models.Event) (responses []models.Event) {
|
||||
if err != nil {
|
||||
log.Println("Error updating state for device", device.ID, "err:", err)
|
||||
}
|
||||
|
||||
if action.SetScene != nil {
|
||||
action.SetScene.StartTime = time.Now()
|
||||
allDevices[i].SceneAssignments = []models.DeviceSceneAssignment{*action.SetScene}
|
||||
}
|
||||
if action.PushScene != nil {
|
||||
action.PushScene.StartTime = time.Now()
|
||||
allDevices[i].SceneAssignments = append(allDevices[i].SceneAssignments, *action.PushScene)
|
||||
}
|
||||
}
|
||||
|
||||
config.PublishChannel <- allDevices
|
||||
|
||||
@@ -19,6 +19,17 @@ type Publisher struct {
|
||||
waiting map[int]chan struct{}
|
||||
}
|
||||
|
||||
func (p *Publisher) SceneState(deviceID int) *models.DeviceState {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
if s := p.sceneAssignment[deviceID]; s != nil {
|
||||
return s.LastState(deviceID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Publisher) UpdateScene(data models.Scene) {
|
||||
p.mu.Lock()
|
||||
p.sceneData[data.ID] = &data
|
||||
@@ -127,7 +138,7 @@ func (p *Publisher) Run() {
|
||||
for _, i := range deleteList {
|
||||
p.scenes = append(p.scenes[:i], p.scenes[i+1:]...)
|
||||
}
|
||||
|
||||
|
||||
for _, device := range updatedList {
|
||||
if !p.started[device.BridgeID] {
|
||||
p.started[device.BridgeID] = true
|
||||
@@ -193,6 +204,7 @@ func (p *Publisher) reassignDevice(device models.Device) bool {
|
||||
endTime: selectedAssignment.StartTime.Add(time.Duration(selectedAssignment.DurationMS) * time.Millisecond),
|
||||
roleMap: make(map[int]int, 16),
|
||||
roleList: make(map[int][]models.Device, 16),
|
||||
lastStates: make(map[int]models.DeviceState, 16),
|
||||
due: true,
|
||||
}
|
||||
p.sceneAssignment[device.ID] = newScene
|
||||
@@ -281,7 +293,7 @@ func Global() *Publisher {
|
||||
}
|
||||
|
||||
func Initialize(ctx context.Context) error {
|
||||
err :=publisher.ReloadScenes(ctx)
|
||||
err := publisher.ReloadScenes(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -290,8 +302,9 @@ func Initialize(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
go publisher.PublishChannel(config.PublishChannel)
|
||||
go publisher.Run()
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
go publisher.PublishChannel(config.PublishChannel)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,12 +6,13 @@ import (
|
||||
)
|
||||
|
||||
type Scene struct {
|
||||
data *models.Scene
|
||||
group string
|
||||
startTime time.Time
|
||||
endTime time.Time
|
||||
roleMap map[int]int
|
||||
roleList map[int][]models.Device
|
||||
data *models.Scene
|
||||
group string
|
||||
startTime time.Time
|
||||
endTime time.Time
|
||||
roleMap map[int]int
|
||||
roleList map[int][]models.Device
|
||||
lastStates map[int]models.DeviceState
|
||||
|
||||
due bool
|
||||
lastInterval int64
|
||||
@@ -84,6 +85,7 @@ func (s *Scene) RemoveDevice(device models.Device) {
|
||||
s.due = true
|
||||
|
||||
delete(s.roleMap, device.ID)
|
||||
delete(s.lastStates, device.ID)
|
||||
}
|
||||
|
||||
func (s *Scene) Empty() bool {
|
||||
@@ -96,7 +98,6 @@ func (s *Scene) Empty() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
func (s *Scene) Due() bool {
|
||||
if s.due {
|
||||
return true
|
||||
@@ -163,6 +164,8 @@ func (s *Scene) Run() []models.Device {
|
||||
continue
|
||||
}
|
||||
|
||||
s.lastStates[device.ID] = device.State
|
||||
|
||||
updatedDevices = append(updatedDevices, device)
|
||||
}
|
||||
}
|
||||
@@ -172,3 +175,12 @@ func (s *Scene) Run() []models.Device {
|
||||
|
||||
return updatedDevices
|
||||
}
|
||||
|
||||
func (s *Scene) LastState(id int) *models.DeviceState {
|
||||
lastState, ok := s.lastStates[id]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &lastState
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Param struct {
|
||||
@@ -221,6 +222,17 @@ func (p Params) DeviceState(prefix string) models.NewDeviceState {
|
||||
}
|
||||
}
|
||||
|
||||
func (p Params) SceneAssignment(prefix string) *models.DeviceSceneAssignment {
|
||||
if p.Get(prefix+"scene") == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &models.DeviceSceneAssignment{
|
||||
SceneID: p.Get(prefix+"scene").IntOr(-1),
|
||||
Group: p.Get(prefix+"scene.group").StringOr(time.Now().Format(time.RFC3339)),
|
||||
DurationMS: int64(p.Get(prefix+"scene.duration").IntOr(0)),
|
||||
}
|
||||
}
|
||||
|
||||
type Command struct {
|
||||
Name string
|
||||
|
||||
@@ -99,7 +99,7 @@ func handlerCmd(
|
||||
func applyCmdToHandler(model models.EventHandler, cmd Command) models.EventHandler {
|
||||
// Remove keys
|
||||
for _, elem := range cmd.Params.Strings(1) {
|
||||
if elem[0] != '-' {
|
||||
if !strings.HasPrefix(elem, "-") {
|
||||
continue
|
||||
}
|
||||
keyToRemove := elem[1:]
|
||||
@@ -128,6 +128,10 @@ func applyCmdToHandler(model models.EventHandler, cmd Command) models.EventHandl
|
||||
model.From = models.Never
|
||||
case "to":
|
||||
model.To = models.Never
|
||||
case "set-scene":
|
||||
model.Actions.SetScene = nil
|
||||
case "push-scene":
|
||||
model.Actions.PushScene = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,5 +177,9 @@ func applyCmdToHandler(model models.EventHandler, cmd Command) models.EventHandl
|
||||
}
|
||||
}
|
||||
|
||||
// Scenes
|
||||
model.Actions.SetScene = cmd.Params.SceneAssignment("set-")
|
||||
model.Actions.PushScene = cmd.Params.SceneAssignment("push-")
|
||||
|
||||
return model
|
||||
}
|
||||
|
||||
+16
-2
@@ -79,7 +79,7 @@ func sceneCmd(
|
||||
}
|
||||
}
|
||||
|
||||
case "assign":
|
||||
case "push", "assign":
|
||||
{
|
||||
fetch := cmd.Params.Get(0).String()
|
||||
id := cmd.Params.Get(1).Int()
|
||||
@@ -87,7 +87,7 @@ func sceneCmd(
|
||||
log.Println("Usage: lucy scene assign <fetch> <id> <group=S> <duration=I>")
|
||||
}
|
||||
|
||||
devices, err := c.AssignDevice(ctx, *fetch, models.DeviceSceneAssignment{
|
||||
devices, err := c.AssignDevice(ctx, *fetch, cmd.Name == "push", models.DeviceSceneAssignment{
|
||||
SceneID: *id,
|
||||
Group: cmd.Params.Get("group").StringOr(*fetch),
|
||||
DurationMS: int64(cmd.Params.Get("duration").IntOr(0)),
|
||||
@@ -99,6 +99,20 @@ func sceneCmd(
|
||||
|
||||
WriteDeviceInfoTable(os.Stdout, devices)
|
||||
}
|
||||
|
||||
case "clear":
|
||||
fetch := cmd.Params.Get(0).String()
|
||||
if fetch == nil {
|
||||
log.Println("Usage: lucy scene clear <fetch>")
|
||||
}
|
||||
|
||||
devices, err := c.ClearDevice(ctx, *fetch)
|
||||
if err != nil {
|
||||
log.Println("Could not clear devices:", err)
|
||||
return
|
||||
}
|
||||
|
||||
WriteDeviceInfoTable(os.Stdout, devices)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
-3
@@ -14,16 +14,32 @@ func WriteDeviceStateTable(w io.Writer, devices []models.Device) {
|
||||
table.SetHeader([]string{"ID", "NAME", "POWER", "COLOR", "INTENSITY", "TEMPERATURE"})
|
||||
|
||||
table.SetReflowDuringAutoWrap(true)
|
||||
table.SetColumnAlignment([]int{
|
||||
tablewriter.ALIGN_RIGHT,
|
||||
tablewriter.ALIGN_LEFT,
|
||||
tablewriter.ALIGN_RIGHT,
|
||||
tablewriter.ALIGN_LEFT,
|
||||
tablewriter.ALIGN_RIGHT,
|
||||
tablewriter.ALIGN_RIGHT,
|
||||
})
|
||||
|
||||
for _, v := range devices {
|
||||
powerStr := ""
|
||||
if v.HasCapability(models.DCPower) {
|
||||
powerStr = strconv.FormatBool(v.State.Power)
|
||||
if v.SceneState != nil && v.SceneState.Power != v.State.Power {
|
||||
powerStr = strconv.FormatBool(v.SceneState.Power) + "*"
|
||||
} else {
|
||||
powerStr = strconv.FormatBool(v.State.Power)
|
||||
}
|
||||
}
|
||||
|
||||
colorStr := ""
|
||||
if v.HasCapability(models.DCColorHSK, models.DCColorHS, models.DCColorKelvin) {
|
||||
colorStr = v.State.Color.String()
|
||||
if v.SceneState != nil && v.SceneState.Color.String() != v.State.Color.String() {
|
||||
colorStr = v.SceneState.Color.String() + "*"
|
||||
} else {
|
||||
colorStr = v.State.Color.String()
|
||||
}
|
||||
}
|
||||
|
||||
temperatureString := ""
|
||||
@@ -33,7 +49,11 @@ func WriteDeviceStateTable(w io.Writer, devices []models.Device) {
|
||||
|
||||
intensityString := ""
|
||||
if v.HasCapability(models.DCIntensity) {
|
||||
intensityString = strconv.FormatFloat(v.State.Intensity, 'f', -1, 64)
|
||||
if v.SceneState != nil && v.SceneState.Intensity != v.State.Intensity {
|
||||
intensityString = strconv.FormatFloat(v.SceneState.Intensity, 'g', 2, 64) + "*"
|
||||
} else {
|
||||
intensityString = strconv.FormatFloat(v.State.Intensity, 'f', -1, 64)
|
||||
}
|
||||
}
|
||||
|
||||
table.Append([]string{
|
||||
@@ -104,6 +124,17 @@ func WriteHandlerInfoTable(w io.Writer, handlers []models.EventHandler) {
|
||||
if h.Actions.FireEvent != nil {
|
||||
actionStr += fmt.Sprintf("fireEvent=%s ", (*h.Actions.FireEvent).Name)
|
||||
}
|
||||
if h.Actions.FireEvent != nil {
|
||||
actionStr += fmt.Sprintf("fireEvent=%s ", (*h.Actions.FireEvent).Name)
|
||||
}
|
||||
if h.Actions.SetScene != nil {
|
||||
s := h.Actions.SetScene
|
||||
actionStr += fmt.Sprintf("setScene=(id=%d,group=\"%s\",duration=%d) ", s.SceneID, s.Group, s.DurationMS)
|
||||
}
|
||||
if h.Actions.PushScene != nil {
|
||||
s := h.Actions.PushScene
|
||||
actionStr += fmt.Sprintf("pushScene=(id=%d,group=\"%s\",duration=%d) ", s.SceneID, s.Group, s.DurationMS)
|
||||
}
|
||||
|
||||
eventName := h.EventName
|
||||
if h.OneShot {
|
||||
|
||||
@@ -26,9 +26,11 @@ func (c *ColorValue) String() string {
|
||||
return fmt.Sprintf("k:%d", c.Kelvin)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("hs:%g,%g", c.Hue, c.Saturation)
|
||||
return fmt.Sprintf("hs:%.4g,%.3g", c.Hue, c.Saturation)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func ParseColorValue(raw string) (ColorValue, error) {
|
||||
tokens := strings.SplitN(raw, ":", 2)
|
||||
if len(tokens) != 2 {
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@ type Device struct {
|
||||
DriverProperties map[string]interface{} `json:"driverProperties"`
|
||||
UserProperties map[string]string `json:"userProperties"`
|
||||
SceneAssignments []DeviceSceneAssignment `json:"sceneAssignments"`
|
||||
SceneState *DeviceState `json:"sceneState"`
|
||||
State DeviceState `json:"state"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
@@ -200,7 +201,7 @@ func (s *NewDeviceState) RelativeTo(device Device) NewDeviceState {
|
||||
if s.Color != nil {
|
||||
c, err := ParseColorValue(*s.Color)
|
||||
if err == nil {
|
||||
c.Hue = math.Mod(device.State.Color.Hue + c.Hue, 360)
|
||||
c.Hue = math.Mod(device.State.Color.Hue+c.Hue, 360)
|
||||
c.Saturation *= device.State.Color.Saturation
|
||||
c.Kelvin += device.State.Color.Kelvin
|
||||
}
|
||||
|
||||
+29
-6
@@ -5,6 +5,7 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type EventHandler struct {
|
||||
@@ -168,6 +169,20 @@ func (c *EventCondition) checkDevice(key string, device Device) (matches bool, s
|
||||
}
|
||||
|
||||
return c.matches(strconv.FormatFloat(device.State.Temperature, 'f', -1, 64)), false
|
||||
case "scene":
|
||||
if len(device.SceneAssignments) == 0 {
|
||||
return false, false
|
||||
}
|
||||
|
||||
sceneId := -1
|
||||
for _, assignment := range device.SceneAssignments {
|
||||
duration := time.Duration(assignment.DurationMS) * time.Millisecond
|
||||
if duration <= 0 || time.Now().Before(assignment.StartTime.Add(duration)) {
|
||||
sceneId = assignment.SceneID
|
||||
}
|
||||
}
|
||||
|
||||
return c.matches(strconv.Itoa(sceneId)), false
|
||||
default:
|
||||
return false, true
|
||||
}
|
||||
@@ -214,12 +229,14 @@ func (c *EventCondition) matches(value string) bool {
|
||||
}
|
||||
|
||||
type EventAction struct {
|
||||
SetPower *bool `json:"setPower"`
|
||||
SetColor *string `json:"setColor"`
|
||||
SetIntensity *float64 `json:"setIntensity"`
|
||||
SetTemperature *int `json:"setTemperature"`
|
||||
AddIntensity *float64 `json:"addIntensity"`
|
||||
FireEvent *Event `json:"fireEvent"`
|
||||
SetPower *bool `json:"setPower"`
|
||||
SetColor *string `json:"setColor"`
|
||||
SetIntensity *float64 `json:"setIntensity"`
|
||||
SetTemperature *int `json:"setTemperature"`
|
||||
AddIntensity *float64 `json:"addIntensity"`
|
||||
FireEvent *Event `json:"fireEvent"`
|
||||
SetScene *DeviceSceneAssignment `json:"setScene"`
|
||||
PushScene *DeviceSceneAssignment `json:"pushScene"`
|
||||
}
|
||||
|
||||
func (action *EventAction) Apply(other EventAction) {
|
||||
@@ -238,6 +255,12 @@ func (action *EventAction) Apply(other EventAction) {
|
||||
if action.FireEvent == nil {
|
||||
action.FireEvent = other.FireEvent
|
||||
}
|
||||
if action.SetScene == nil {
|
||||
action.SetScene = other.SetScene
|
||||
}
|
||||
if action.PushScene == nil {
|
||||
action.PushScene = other.PushScene
|
||||
}
|
||||
}
|
||||
|
||||
func (c EventCondition) String() string {
|
||||
|
||||
+8
-2
@@ -114,7 +114,7 @@ func (r *SceneRole) Validate() error {
|
||||
}
|
||||
|
||||
switch r.PowerMode {
|
||||
case SPScene, SPDevice:
|
||||
case SPScene, SPDevice, SPBoth:
|
||||
default:
|
||||
return ErrSceneRoleUnknownPowerMode
|
||||
}
|
||||
@@ -188,7 +188,12 @@ func (r *SceneRole) ApplyEffect(device *Device, c SceneRunContext) (newState New
|
||||
case SPDevice:
|
||||
newState.Power = nil
|
||||
case SPScene:
|
||||
// Do nothing
|
||||
// Do nothing
|
||||
case SPBoth:
|
||||
if newState.Power != nil {
|
||||
powerIntersection := *newState.Power && device.State.Power
|
||||
newState.Power = &powerIntersection
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
@@ -226,6 +231,7 @@ type ScenePowerMode string
|
||||
const (
|
||||
SPDevice ScenePowerMode = "Device" // Device state decides power. Scene state may only power off.
|
||||
SPScene ScenePowerMode = "Scene" // Scene state decide power.
|
||||
SPBoth ScenePowerMode = "Both" // Power is only on if both Device and Scene says it is.
|
||||
)
|
||||
|
||||
// DeviceSceneAssignment is an entry on the device stack. StartTime and DurationMS are only respected when the scene
|
||||
|
||||
@@ -48,8 +48,8 @@ roles:
|
||||
relative: false
|
||||
order: +name
|
||||
states:
|
||||
- color: 'hs:250,0.6'
|
||||
intensity: 0.2
|
||||
- color: 'hs:250,0.7'
|
||||
intensity: 0.25
|
||||
|
||||
- effect: Static
|
||||
power_mode: Device
|
||||
@@ -60,4 +60,4 @@ roles:
|
||||
order: +name
|
||||
states:
|
||||
- color: 'k:2000'
|
||||
intensity: 0.25
|
||||
intensity: 0.3
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
name: Flash
|
||||
interval: 0
|
||||
roles:
|
||||
- effect: Static
|
||||
power_mode: Device
|
||||
target_kind: Tag
|
||||
target_value: Nanoleaf
|
||||
states:
|
||||
- intensity: 1
|
||||
color: hs:220,0.1
|
||||
|
||||
- effect: Static
|
||||
power_mode: Device
|
||||
target_kind: Tag
|
||||
target_value: Hue
|
||||
states:
|
||||
- intensity: 1
|
||||
color: k:6500
|
||||
|
||||
- effect: Static
|
||||
power_mode: Device
|
||||
target_kind: All
|
||||
target_value: ""
|
||||
states:
|
||||
- intensity: 1
|
||||
- intensity: 1
|
||||
color: hs:0,0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: Late
|
||||
interval: 5000
|
||||
interval: 60000
|
||||
roles:
|
||||
- effect: Random
|
||||
power_mode: Device
|
||||
@@ -11,11 +11,15 @@ roles:
|
||||
states:
|
||||
- color: 'hs:35,1'
|
||||
intensity: 0.08
|
||||
- color: 'hs:25,1'
|
||||
intensity: 0.10
|
||||
- color: 'hs:35,1'
|
||||
intensity: 0.085
|
||||
- color: 'hs:35,1'
|
||||
intensity: 0.09
|
||||
- color: 'hs:35,1'
|
||||
intensity: 0.12
|
||||
|
||||
- effect: Gradient
|
||||
power_mode: Device
|
||||
power_mode: Both
|
||||
target_kind: Tag
|
||||
target_value: Square
|
||||
interpolate: true
|
||||
@@ -24,8 +28,13 @@ roles:
|
||||
states:
|
||||
- color: 'hs:25,1'
|
||||
intensity: 0.05
|
||||
power: off
|
||||
- color: 'hs:30,1'
|
||||
intensity: 0.06
|
||||
power: off
|
||||
- color: 'hs:35,1'
|
||||
intensity: 0.05
|
||||
intensity: 0.07
|
||||
power: on
|
||||
|
||||
- effect: Static
|
||||
power_mode: Device
|
||||
@@ -35,8 +44,8 @@ roles:
|
||||
relative: false
|
||||
order: +name
|
||||
states:
|
||||
- color: 'hs:25,1'
|
||||
intensity: 0.05
|
||||
- color: 'k:2000'
|
||||
intensity: 0.15
|
||||
|
||||
- effect: Static
|
||||
power_mode: Scene
|
||||
|
||||
@@ -9,10 +9,10 @@ roles:
|
||||
relative: false
|
||||
order: +name
|
||||
states:
|
||||
- color: 'hs:220,0.5'
|
||||
intensity: 0.50
|
||||
- color: 'hs:220,0.4'
|
||||
intensity: 0.65
|
||||
- color: 'hs:220,0.5'
|
||||
intensity: 0.50
|
||||
|
||||
- effect: Random
|
||||
power_mode: Device
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Saturday
|
||||
interval: 700
|
||||
roles:
|
||||
- effect: WalkingGradient
|
||||
power_mode: Device
|
||||
target_kind: Tag
|
||||
target_value: Hexagon
|
||||
interpolate: true
|
||||
relative: false
|
||||
order: +name
|
||||
states:
|
||||
- color: 'hs:220,0.5'
|
||||
intensity: 0.25
|
||||
- color: 'hs:220,0.5'
|
||||
intensity: 0.30
|
||||
|
||||
- effect: Random
|
||||
power_mode: Device
|
||||
target_kind: Tag
|
||||
target_value: Square
|
||||
interpolate: true
|
||||
relative: false
|
||||
order: +name
|
||||
states:
|
||||
- color: 'hs:220,0.6'
|
||||
intensity: 0.15
|
||||
- color: 'hs:220,0.55'
|
||||
intensity: 0.16
|
||||
- color: 'hs:220,0.5'
|
||||
intensity: 0.20
|
||||
|
||||
- effect: Static
|
||||
power_mode: Device
|
||||
target_kind: All
|
||||
target_value: ''
|
||||
interpolate: true
|
||||
relative: false
|
||||
order: +name
|
||||
states:
|
||||
- color: 'hs:250,0.7'
|
||||
intensity: 0.3
|
||||
|
||||
Reference in New Issue
Block a user