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