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.
194 lines
4.9 KiB
194 lines
4.9 KiB
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"git.aiterp.net/lucifer/new-server/app/config"
|
|
"git.aiterp.net/lucifer/new-server/models"
|
|
"log"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var flagDriver = flag.String("driver", string(models.DTNanoLeaf), "The bridge driver to use")
|
|
var flagAddress = flag.String("address", "127.0.0.1", "The bridge's address")
|
|
var flagToken = flag.String("token", "", "The bridge's access token / api key / login")
|
|
var flagPair = flag.Bool("pair", false, "Try to pair with the bridge.")
|
|
var flagSearch = flag.Bool("search", false, "Search for devices first.")
|
|
var flagSearchTimeout = flag.Duration("search-timeout", time.Second*3, "Timeout for device search.")
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
// Find drivers
|
|
driver, err := config.DriverProvider().Provide(models.DriverKind(*flagDriver))
|
|
if err != nil {
|
|
log.Fatalln("Failed to find driver:", err)
|
|
}
|
|
|
|
// Find bridge
|
|
bridges, err := driver.SearchBridge(context.Background(), *flagAddress, *flagToken, !*flagPair)
|
|
if err != nil {
|
|
log.Fatalln("Failed to search bridge:", err)
|
|
}
|
|
if len(bridges) == 0 {
|
|
log.Fatalln("No bridges found")
|
|
}
|
|
bridge := bridges[0]
|
|
if !*flagPair {
|
|
bridge.Token = *flagToken
|
|
} else {
|
|
log.Println("New token:", bridge.Token)
|
|
}
|
|
|
|
ch := config.EventChannel
|
|
go func() {
|
|
err := driver.Run(context.Background(), bridge, ch)
|
|
if err != nil {
|
|
log.Fatalln("Run bridge stopped:", err)
|
|
}
|
|
}()
|
|
time.Sleep(time.Second)
|
|
|
|
// List devices
|
|
var devices []models.Device
|
|
if *flagSearch {
|
|
devices, err = driver.SearchDevices(context.Background(), bridge, *flagSearchTimeout)
|
|
if err != nil {
|
|
log.Fatalln("Failed to search devices:", err)
|
|
}
|
|
} else {
|
|
devices, err = driver.ListDevices(context.Background(), bridge)
|
|
if err != nil {
|
|
log.Fatalln("Failed to list devices:", err)
|
|
}
|
|
}
|
|
|
|
idMap := make(map[string]int)
|
|
nextId := len(devices) + 1
|
|
for i := range devices {
|
|
devices[i].ID = i + 1
|
|
idMap[devices[i].InternalID] = i + 1
|
|
}
|
|
|
|
go func() {
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
_, _ = fmt.Fprintln(os.Stderr, "Format: [id1,id2,...] [on|off] [color] [intensity]")
|
|
for _, device := range devices {
|
|
_, _ = fmt.Fprintf(os.Stderr, "Device: %d - %s %+v\n", device.ID, device.InternalID, device.Capabilities)
|
|
}
|
|
_, _ = fmt.Fprintln(os.Stderr, "Format: [id1,id2,...] [on|off] [color]")
|
|
|
|
for {
|
|
text, _ := reader.ReadString('\n')
|
|
text = strings.Trim(text, "\t \r\n")
|
|
|
|
if text == "search" {
|
|
_, _ = driver.SearchDevices(context.Background(), bridge, time.Second)
|
|
}
|
|
if text == "list" || text == "search" || text == "json" {
|
|
devices, err = driver.ListDevices(context.Background(), bridge)
|
|
if err != nil {
|
|
log.Fatalln("Failed to list devices:", err)
|
|
}
|
|
|
|
for i, device := range devices {
|
|
if extId, ok := idMap[device.InternalID]; ok {
|
|
devices[i].ID = extId
|
|
} else {
|
|
idMap[device.InternalID] = nextId
|
|
devices[i].ID = nextId
|
|
|
|
nextId += 1
|
|
}
|
|
|
|
if text != "json" {
|
|
_, _ = fmt.Fprintf(os.Stderr, "Device: %d - %s %+v\n", device.ID, device.InternalID, device.Capabilities)
|
|
}
|
|
}
|
|
}
|
|
if text == "json" {
|
|
j, _ := json.MarshalIndent(devices, "", " ")
|
|
fmt.Println(string(j))
|
|
}
|
|
|
|
tokens := strings.Split(text, " ")
|
|
if len(tokens) < 4 {
|
|
continue
|
|
}
|
|
|
|
color, err := models.ParseColorValue(tokens[2])
|
|
if err != nil {
|
|
_, _ = fmt.Fprintln(os.Stderr, "Invalid color:", err)
|
|
continue
|
|
}
|
|
|
|
intensity, _ := strconv.ParseFloat(tokens[3], 64)
|
|
|
|
power := strings.ToLower(tokens[1]) == "on"
|
|
|
|
idsStr := strings.Split(tokens[0], ",")
|
|
ids := make([]int, 0, len(idsStr))
|
|
for _, idStr := range idsStr {
|
|
if idStr == "*" {
|
|
ids = append(ids[:0], -1)
|
|
break
|
|
}
|
|
|
|
id, err := strconv.Atoi(idStr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
ids = append(ids, id)
|
|
}
|
|
|
|
updatedDevices := devices[:0:0]
|
|
for _, device := range devices {
|
|
for _, id := range ids {
|
|
if id == -1 || id == device.ID {
|
|
if (color.IsKelvin() && device.HasCapability(models.DCColorKelvin)) || (color.IsHueSat() && device.HasCapability(models.DCColorHS)) {
|
|
device.State.Color = color
|
|
}
|
|
if device.HasCapability(models.DCPower) {
|
|
device.State.Power = power
|
|
}
|
|
if device.HasCapability(models.DCIntensity) {
|
|
device.State.Intensity = intensity
|
|
}
|
|
|
|
updatedDevices = append(updatedDevices, device)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(updatedDevices) > 0 {
|
|
err := driver.Publish(context.Background(), bridge, updatedDevices)
|
|
if err != nil {
|
|
log.Fatalln("Publish to bridge failed:", err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
for event := range ch {
|
|
_, _ = fmt.Fprintf(os.Stderr, "Event %s", event.Name)
|
|
keys := make([]string, 0, 8)
|
|
for key := range event.Payload {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, key := range keys {
|
|
_, _ = fmt.Fprintf(os.Stderr, " %s=%#+v", key, event.Payload[key])
|
|
}
|
|
_, _ = fmt.Fprint(os.Stderr, "\n")
|
|
}
|
|
}
|