diff --git a/app/api/devices.go b/app/api/devices.go index 0438514..c524b05 100644 --- a/app/api/devices.go +++ b/app/api/devices.go @@ -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] + err = config.DeviceRepository().SaveMany(ctxOf(c), 0, devices) + if err != nil { + return nil, err + } - eg.Go(func() error { - return config.DeviceRepository().Save(ctxOf(c), &device, 0) - }) + 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 = eg.Wait() + err = config.DeviceRepository().SaveMany(ctxOf(c), 0, devices) if err != nil { return nil, err } - return devices, nil + 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 +} diff --git a/app/client/client.go b/app/client/client.go index 84b8dcb..c0bba26 100644 --- a/app/client/client.go +++ b/app/client/client.go @@ -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"+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, "PUT", "/api/devices/"+fetchStr+"/scene", &devices, assignment) + err := client.Fetch(ctx, "DELETE", "/api/devices/"+fetchStr+"/scene", &devices, nil) if err != nil { return nil, err } diff --git a/app/client/handler.go b/app/client/handler.go index 4ef960c..fdc0ab4 100644 --- a/app/client/handler.go +++ b/app/client/handler.go @@ -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 } diff --git a/app/services/bridges.go b/app/services/bridges.go index 14f82e1..ea4df28 100644 --- a/app/services/bridges.go +++ b/app/services/bridges.go @@ -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) diff --git a/app/services/events.go b/app/services/events.go index 3079599..9e86300 100644 --- a/app/services/events.go +++ b/app/services/events.go @@ -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 diff --git a/app/services/publisher/publisher.go b/app/services/publisher/publisher.go index e181e34..2b3f604 100644 --- a/app/services/publisher/publisher.go +++ b/app/services/publisher/publisher.go @@ -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 } diff --git a/app/services/publisher/scene.go b/app/services/publisher/scene.go index eea9cfa..e6d605d 100644 --- a/app/services/publisher/scene.go +++ b/app/services/publisher/scene.go @@ -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 +} diff --git a/cmd/lucy/command.go b/cmd/lucy/command.go index 2648711..fe7aab1 100644 --- a/cmd/lucy/command.go +++ b/cmd/lucy/command.go @@ -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 diff --git a/cmd/lucy/handlercmd.go b/cmd/lucy/handlercmd.go index 9a65def..05a04d2 100644 --- a/cmd/lucy/handlercmd.go +++ b/cmd/lucy/handlercmd.go @@ -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 } diff --git a/cmd/lucy/scenecmd.go b/cmd/lucy/scenecmd.go index 63675be..31139d4 100644 --- a/cmd/lucy/scenecmd.go +++ b/cmd/lucy/scenecmd.go @@ -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 ") } - 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 ") + } + + devices, err := c.ClearDevice(ctx, *fetch) + if err != nil { + log.Println("Could not clear devices:", err) + return + } + + WriteDeviceInfoTable(os.Stdout, devices) } } diff --git a/cmd/lucy/tables.go b/cmd/lucy/tables.go index cb3a15b..7808ca4 100644 --- a/cmd/lucy/tables.go +++ b/cmd/lucy/tables.go @@ -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 { diff --git a/models/colorvalue.go b/models/colorvalue.go index 0bfff3e..119e170 100644 --- a/models/colorvalue.go +++ b/models/colorvalue.go @@ -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 { diff --git a/models/device.go b/models/device.go index bf1abec..aa76fe2 100644 --- a/models/device.go +++ b/models/device.go @@ -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 } diff --git a/models/eventhandler.go b/models/eventhandler.go index 31a2b7e..0a7ea4f 100644 --- a/models/eventhandler.go +++ b/models/eventhandler.go @@ -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 { diff --git a/models/scene.go b/models/scene.go index 64a0c72..7dd215e 100644 --- a/models/scene.go +++ b/models/scene.go @@ -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 diff --git a/scene-examples/evening.yaml b/scene-examples/evening.yaml index a54d12f..825b83f 100644 --- a/scene-examples/evening.yaml +++ b/scene-examples/evening.yaml @@ -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 diff --git a/scene-examples/flash.yaml b/scene-examples/flash.yaml index 94a916d..dc5ed51 100644 --- a/scene-examples/flash.yaml +++ b/scene-examples/flash.yaml @@ -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 \ No newline at end of file + - intensity: 1 + color: hs:0,0 diff --git a/scene-examples/late.yaml b/scene-examples/late.yaml index 8441033..c6d4613 100644 --- a/scene-examples/late.yaml +++ b/scene-examples/late.yaml @@ -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 diff --git a/scene-examples/morning.yaml b/scene-examples/morning.yaml index 6a1901e..52432b4 100644 --- a/scene-examples/morning.yaml +++ b/scene-examples/morning.yaml @@ -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 diff --git a/scene-examples/saturday.yaml b/scene-examples/saturday.yaml new file mode 100644 index 0000000..7ee87d4 --- /dev/null +++ b/scene-examples/saturday.yaml @@ -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 +