Browse Source
more scene stuff. There's still no command and API to push scenes and unassign (aside from setting a temporary scene and waiting it out).
pull/1/head
more scene stuff. There's still no command and API to push scenes and unassign (aside from setting a temporary scene and waiting it out).
pull/1/head
Gisle Aune
3 years ago
14 changed files with 416 additions and 9 deletions
-
31app/api/scenes.go
-
10app/client/client.go
-
16app/client/scene.go
-
28app/services/publisher/publisher.go
-
11app/services/publisher/scene.go
-
3cmd/lucy/main.go
-
151cmd/lucy/scenecmd.go
-
2internal/drivers/nanoleaf/bridge.go
-
5internal/mysql/scenerepo.go
-
6models/scene.go
-
63scene-examples/evening.yaml
-
9scene-examples/flash.yaml
-
48scene-examples/late.yaml
-
40scene-examples/morning.yaml
@ -0,0 +1,16 @@ |
|||
package client |
|||
|
|||
import ( |
|||
"context" |
|||
"git.aiterp.net/lucifer/new-server/models" |
|||
) |
|||
|
|||
func (client *Client) GetScenes(ctx context.Context) ([]models.Scene, error) { |
|||
scenes := make([]models.Scene, 0, 16) |
|||
err := client.Fetch(ctx, "GET", "/api/scenes", &scenes, nil) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return scenes, nil |
|||
} |
@ -0,0 +1,151 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"context" |
|||
"git.aiterp.net/lucifer/new-server/app/client" |
|||
"git.aiterp.net/lucifer/new-server/models" |
|||
"gopkg.in/yaml.v2" |
|||
"log" |
|||
"os" |
|||
"strconv" |
|||
"strings" |
|||
"unicode" |
|||
) |
|||
|
|||
func sceneCmd( |
|||
ctx context.Context, |
|||
c client.Client, |
|||
) { |
|||
cmd := parseCommand(os.Args[2:]) |
|||
|
|||
switch cmd.Name { |
|||
case "create", "update": |
|||
{ |
|||
fileName := cmd.Params.Get(0).String() |
|||
if fileName == nil { |
|||
log.Fatalln("Missing filename") |
|||
} |
|||
|
|||
file, err := os.Open(*fileName) |
|||
if err != nil { |
|||
log.Fatalln("Failed to open file:", err) |
|||
} |
|||
defer file.Close() |
|||
|
|||
yamlData := make(map[string]interface{}) |
|||
err = yaml.NewDecoder(file).Decode(yamlData) |
|||
if err != nil { |
|||
log.Fatalln("Failed to decode file:", err) |
|||
return |
|||
} |
|||
|
|||
yamlData = camelCasify(yamlData) |
|||
name, nameOk := yamlData["name"] |
|||
if !nameOk { |
|||
log.Fatalln("Missing name in yaml data.") |
|||
} |
|||
|
|||
var scene models.Scene |
|||
if cmd.Name == "create" { |
|||
err := c.Fetch(ctx, "POST", "/api/scenes", &scene, yamlData) |
|||
if err != nil { |
|||
log.Fatalln("Failed to create scene:", err) |
|||
return |
|||
} |
|||
} else { |
|||
scenes, err := c.GetScenes(ctx) |
|||
if err != nil { |
|||
log.Fatalln("Failed to fetch existing scenes:", err) |
|||
return |
|||
} |
|||
|
|||
id := -1 |
|||
for _, scene := range scenes { |
|||
if scene.Name == name { |
|||
id = scene.ID |
|||
break |
|||
} |
|||
} |
|||
if id == -1 { |
|||
log.Fatalln("Could not find scene with name", name) |
|||
return |
|||
} |
|||
|
|||
err = c.Fetch(ctx, "PUT", "/api/scenes/"+strconv.Itoa(id), &scene, yamlData) |
|||
if err != nil { |
|||
log.Fatalln("Failed to update scene:", err) |
|||
return |
|||
} |
|||
} |
|||
} |
|||
|
|||
case "assign": |
|||
{ |
|||
fetch := cmd.Params.Get(0).String() |
|||
id := cmd.Params.Get(1).Int() |
|||
if fetch == nil || id == nil { |
|||
log.Println("Usage: lucy scene assign <fetch> <id> <group=S> <duration=I>") |
|||
} |
|||
|
|||
devices, err := c.AssignDevice(ctx, *fetch, models.DeviceSceneAssignment{ |
|||
SceneID: *id, |
|||
Group: cmd.Params.Get("group").StringOr(*fetch), |
|||
DurationMS: int64(cmd.Params.Get("duration").IntOr(0)), |
|||
}) |
|||
if err != nil { |
|||
log.Println("Could not assign devices:", err) |
|||
return |
|||
} |
|||
|
|||
WriteDeviceInfoTable(os.Stdout, devices) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func camelCasify(m map[string]interface{}) map[string]interface{} { |
|||
m2 := make(map[string]interface{}, len(m)) |
|||
for key, value := range m { |
|||
b := strings.Builder{} |
|||
snake := false |
|||
for _, ch := range key { |
|||
if ch == '_' { |
|||
snake = true |
|||
} else if snake { |
|||
b.WriteRune(unicode.ToUpper(ch)) |
|||
snake = false |
|||
} else { |
|||
b.WriteRune(ch) |
|||
} |
|||
} |
|||
|
|||
switch value := value.(type) { |
|||
case []interface{}: |
|||
valueCopy := make([]interface{}, len(value)) |
|||
for i, elem := range value { |
|||
switch elem := elem.(type) { |
|||
case map[interface{}]interface{}: |
|||
m3 := make(map[string]interface{}) |
|||
for k, v := range elem { |
|||
if kStr, ok := k.(string); ok { |
|||
m3[kStr] = v |
|||
} |
|||
} |
|||
|
|||
valueCopy[i] = camelCasify(m3) |
|||
case map[string]interface{}: |
|||
valueCopy[i] = camelCasify(elem) |
|||
default: |
|||
valueCopy[i] = elem |
|||
} |
|||
} |
|||
|
|||
m2[b.String()] = valueCopy |
|||
case map[string]interface{}: |
|||
m2[b.String()] = camelCasify(value) |
|||
default: |
|||
m2[b.String()] = value |
|||
} |
|||
} |
|||
|
|||
return m2 |
|||
} |
@ -0,0 +1,63 @@ |
|||
name: Evening |
|||
interval: 1500 |
|||
roles: |
|||
- effect: WalkingGradient |
|||
power_mode: Device |
|||
target_kind: Tag |
|||
target_value: Hexagon |
|||
interpolate: true |
|||
relative: false |
|||
order: -name |
|||
states: |
|||
- color: 'hs:30,1' |
|||
intensity: 0.20 |
|||
- color: 'hs:30,1' |
|||
intensity: 0.15 |
|||
|
|||
- effect: Gradient |
|||
power_mode: Device |
|||
target_kind: Tag |
|||
target_value: SquareLeft |
|||
interpolate: true |
|||
relative: false |
|||
order: +name |
|||
states: |
|||
- color: 'hs:25,1' |
|||
intensity: 0.075 |
|||
- color: 'hs:50,1' |
|||
intensity: 0.15 |
|||
|
|||
- effect: Random |
|||
power_mode: Device |
|||
target_kind: Tag |
|||
target_value: SquareRight |
|||
interpolate: true |
|||
relative: false |
|||
order: +name |
|||
states: |
|||
- color: 'hs:220,0.7' |
|||
intensity: 0.22 |
|||
- color: 'hs:220,0.6' |
|||
intensity: 0.25 |
|||
|
|||
- effect: Static |
|||
power_mode: Device |
|||
target_kind: Tag |
|||
target_value: Accent |
|||
interpolate: true |
|||
relative: false |
|||
order: +name |
|||
states: |
|||
- color: 'hs:250,0.6' |
|||
intensity: 0.2 |
|||
|
|||
- effect: Static |
|||
power_mode: Device |
|||
target_kind: All |
|||
target_value: "" |
|||
interpolate: true |
|||
relative: false |
|||
order: +name |
|||
states: |
|||
- color: 'k:2000' |
|||
intensity: 0.25 |
@ -0,0 +1,9 @@ |
|||
name: Flash |
|||
interval: 0 |
|||
roles: |
|||
- effect: Static |
|||
power_mode: Device |
|||
target_kind: All |
|||
target_value: "" |
|||
states: |
|||
- intensity: 1 |
@ -0,0 +1,48 @@ |
|||
name: Late |
|||
interval: 5000 |
|||
roles: |
|||
- effect: Random |
|||
power_mode: Device |
|||
target_kind: Tag |
|||
target_value: Hexagon |
|||
interpolate: true |
|||
relative: false |
|||
order: +name |
|||
states: |
|||
- color: 'hs:35,1' |
|||
intensity: 0.08 |
|||
- color: 'hs:25,1' |
|||
intensity: 0.10 |
|||
|
|||
- effect: Gradient |
|||
power_mode: Device |
|||
target_kind: Tag |
|||
target_value: Square |
|||
interpolate: true |
|||
relative: false |
|||
order: +name |
|||
states: |
|||
- color: 'hs:25,1' |
|||
intensity: 0.05 |
|||
- color: 'hs:35,1' |
|||
intensity: 0.05 |
|||
|
|||
- effect: Static |
|||
power_mode: Device |
|||
target_kind: Tag |
|||
target_value: Nightstand |
|||
interpolate: true |
|||
relative: false |
|||
order: +name |
|||
states: |
|||
- color: 'hs:25,1' |
|||
intensity: 0.05 |
|||
|
|||
- effect: Static |
|||
power_mode: Scene |
|||
target_kind: All |
|||
target_value: '' |
|||
interpolate: true |
|||
relative: false |
|||
states: |
|||
- power: false |
@ -0,0 +1,40 @@ |
|||
name: Morning |
|||
interval: 1000 |
|||
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.50 |
|||
- color: 'hs:220,0.4' |
|||
intensity: 0.65 |
|||
|
|||
- 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.30 |
|||
- color: 'hs:220,0.5' |
|||
intensity: 0.35 |
|||
|
|||
- effect: Static |
|||
power_mode: Device |
|||
target_kind: All |
|||
target_value: '' |
|||
interpolate: true |
|||
relative: false |
|||
order: +name |
|||
states: |
|||
- color: 'hs:250,0.6' |
|||
intensity: 0.3 |
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue