Feat: launch multiple commands inline concurrently and synchronously (bash-like, &&, &, ;)

plus other stuff prolly
This commit is contained in:
kayos@tcp.direct 2023-06-04 05:13:50 -07:00
parent aca5f81f3b
commit 1cdb1c33cb
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
15 changed files with 187 additions and 100 deletions

2
go.mod

@ -19,7 +19,7 @@ require (
github.com/muesli/termenv v0.15.1 github.com/muesli/termenv v0.15.1
github.com/rs/zerolog v1.29.1 github.com/rs/zerolog v1.29.1
github.com/spf13/viper v1.15.0 github.com/spf13/viper v1.15.0
github.com/yunginnanet/huego v1.2.2-0.20230529010539-d408ab817fd4 github.com/yunginnanet/huego v1.2.2-0.20230529014403-8b8a50edf010
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35 go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
golang.org/x/crypto v0.9.0 golang.org/x/crypto v0.9.0
golang.org/x/net v0.10.0 golang.org/x/net v0.10.0

4
go.sum

@ -438,8 +438,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yunginnanet/huego v1.2.2-0.20230529010539-d408ab817fd4 h1:ygkYBx4P1MZPuCLpCf4tVCGroemO5vLGpukwsOi5aQU= github.com/yunginnanet/huego v1.2.2-0.20230529014403-8b8a50edf010 h1:hZYdjxY9hM/8e82rv8x0C56prvoBjLNWJToytTtxMEc=
github.com/yunginnanet/huego v1.2.2-0.20230529010539-d408ab817fd4/go.mod h1:geoV/kOgrqHaEeT2QdOfqrvyTNSLs9JVN6Wzsk5zss0= github.com/yunginnanet/huego v1.2.2-0.20230529014403-8b8a50edf010/go.mod h1:geoV/kOgrqHaEeT2QdOfqrvyTNSLs9JVN6Wzsk5zss0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=

@ -7,11 +7,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
"time" "time"
cli "git.tcp.direct/Mirrors/go-prompt" cli "git.tcp.direct/Mirrors/go-prompt"
"github.com/davecgh/go-spew/spew"
tui "github.com/manifoldco/promptui"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/google/shlex" "github.com/google/shlex"
@ -30,7 +29,16 @@ var (
var noHist = map[string]bool{"clear": true, "exit": true, "quit": true} var noHist = map[string]bool{"clear": true, "exit": true, "quit": true}
// Interpret is where we will actuall define our Commands // Interpret is where we will actuall define our Commands
func executor(cmd string) { func Executor(cmd string) {
if log == nil {
log = config.StartLogger()
}
log.Trace().Caller().Msg("getting readlock for suggestions")
SuggestionMutex.RLock()
defer SuggestionMutex.RUnlock()
log.Trace().Caller().Msg("got readlock for suggestions")
var status = 0 var status = 0
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -76,7 +84,7 @@ func executor(cmd string) {
} }
sel.Bridge = args[1] sel.Bridge = args[1]
log.Info().Str("host", br.Host).Int("lights", len(br.HueLights)).Msg("switched to bridge: " + sel.Bridge) log.Info().Str("host", br.Host).Int("lights", len(br.HueLights)).Msg("switched to bridge: " + sel.Bridge)
return
case "debug": case "debug":
levelsdebug := map[string]zerolog.Level{"info": zerolog.InfoLevel, "debug": zerolog.DebugLevel, "trace": zerolog.TraceLevel} levelsdebug := map[string]zerolog.Level{"info": zerolog.InfoLevel, "debug": zerolog.DebugLevel, "trace": zerolog.TraceLevel}
debuglevels := map[zerolog.Level]string{zerolog.InfoLevel: "info", zerolog.DebugLevel: "debug", zerolog.TraceLevel: "trace"} debuglevels := map[zerolog.Level]string{zerolog.InfoLevel: "info", zerolog.DebugLevel: "debug", zerolog.TraceLevel: "trace"}
@ -94,8 +102,8 @@ func executor(cmd string) {
log.Info().Msg("disabled cli debug") log.Info().Msg("disabled cli debug")
} else { } else {
extraDebug = true extraDebug = true
log.Info().Msgf("dumping suggestions") /* log.Info().Msgf("dumping suggestions")
spew.Dump(suggestions) spew.Dump(suggestions)*/
log.Info().Msg("enabled cli debug") log.Info().Msg("enabled cli debug")
} }
return return
@ -109,44 +117,62 @@ func executor(cmd string) {
getHelp(args[len(args)-1]) getHelp(args[len(args)-1])
case "clear": case "clear":
print("\033[H\033[2J") print("\033[H\033[2J")
return
default: default:
if len(args) == 0 { if len(args) == 0 {
return return
} }
bcmd, ok := Commands[args[0]]
if !ok { complete := strings.Join(args, " ")
log.Error().Msg("invalid command: " + args[0]) log.Trace().Caller().Msgf("complete command: %s", complete)
status = 1 if strings.Contains(complete, "&&") {
return log.Warn().Caller().Msgf("found \"&&\" in command: %s, replacing with \";\"", complete)
strings.ReplaceAll(complete, "&&", ";")
} }
sep := strings.Split(complete, "&")
log.Trace().Caller().Msgf("sep: %+s", sep)
br, ok := ziggy.Lucifer.Bridges[sel.Bridge] br, ok := ziggy.Lucifer.Bridges[sel.Bridge]
if sel.Bridge == "" || !ok { if sel.Bridge == "" || !ok {
q := tui.Select{ for _, b := range ziggy.Lucifer.Bridges {
Label: "Send to all known bridges?", br = ziggy.Lucifer.Bridges[b.Info.IPAddress]
Items: []string{"yes", "no"}, break
Pointer: common.ZiggsPointer,
} }
_, ch, _ := q.Run()
if ch != "yes" {
return
}
for _, br := range ziggy.Lucifer.Bridges {
go func(brj *ziggy.Bridge) {
err := bcmd.reactor(brj, args[1:])
if err != nil {
log.Error().Err(err).Msg("bridge command failed")
}
}(br)
} }
wg := &sync.WaitGroup{}
for _, cm := range sep {
cm = strings.TrimSpace(cm)
log.Trace().Caller().Msgf("executing command: %s", cm)
wg.Add(1)
go func(c string) {
c = strings.TrimSpace(c)
defer wg.Done()
for _, synchro := range strings.Split(c, ";") {
synchro = strings.TrimSpace(synchro)
myArgs := strings.Split(synchro, " ")
bcmd, myok := Commands[myArgs[0]]
if !myok {
log.Error().Msg("invalid command: " + myArgs[0])
status = 1
return return
} }
err := bcmd.reactor(br, args[1:]) log.Trace().Caller().Msgf("selected bridge: %s", sel.Bridge)
if err != nil {
log.Error().Err(err).Msg("error executing command") if e := bcmd.reactor(br, myArgs[1:]); e != nil {
log.Error().Msgf("error executing command: %s", e)
status = 1 status = 1
return
} }
} }
}(cm)
}
wg.Wait()
}
} }
func cmdScan(br *ziggy.Bridge, args []string) error { func cmdScan(br *ziggy.Bridge, args []string) error {
@ -204,7 +230,7 @@ func loadHist() {
continue continue
default: default:
histMap[strings.TrimSpace(xerox.Text())] = true histMap[strings.TrimSpace(xerox.Text())] = true
history = append(history, xerox.Text()) history = append(history, strings.ReplaceAll(xerox.Text(), "_POUNDSIGN_", "#"))
} }
} }
histLoaded = true histLoaded = true
@ -224,18 +250,10 @@ func saveHist() {
// func StartCLI(r io.Reader, w io.Writer) { // func StartCLI(r io.Reader, w io.Writer) {
func StartCLI() { func StartCLI() {
log = config.GetLogger() ct, _ := common.Version()
processBridges()
go func() {
processGroups(ziggy.GetGroupMap())
processLights(ziggy.GetLightMap())
processScenes(ziggy.GetSceneMap())
}()
buildTime, _ := common.Version()
// cli.NewStdoutWriter(). // cli.NewStdoutWriter().
prompt = cli.New( prompt = cli.New(
executor, Executor,
completer, completer,
// cli.OptionWriter(w), // cli.OptionWriter(w),
// cli.Op(r), // cli.Op(r),
@ -255,7 +273,8 @@ func StartCLI() {
} }
return fmt.Sprintf("ziggs[%s] %s ", sel.String(), bulb), true return fmt.Sprintf("ziggs[%s] %s ", sel.String(), bulb), true
}), }),
cli.OptionTitle("ziggs - built "+buildTime),
cli.OptionTitle("ziggs - built "+ct),
) )
prompt.Run() prompt.Run()

@ -11,8 +11,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/yunginnanet/huego"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/yunginnanet/huego"
"git.tcp.direct/kayos/ziggs/internal/ziggy" "git.tcp.direct/kayos/ziggs/internal/ziggy"
) )
@ -85,7 +85,7 @@ func cmdScenes(br *ziggy.Bridge, args []string) error {
for _, scene := range scenes { for _, scene := range scenes {
log.Info().Str("caller", strings.Split(br.Host, "://")[1]). log.Info().Str("caller", strings.Split(br.Host, "://")[1]).
Str("ID", scene.ID).Msgf("Scene: %s", scene.Name) Str("ID", scene.ID).Msgf("Scene: %s", scene.Name)
log.Trace().Msgf("%v", spew.Sprint(scene)) log.Trace().Caller().Msgf("%v", spew.Sprint(scene))
} }
return nil return nil
} }
@ -95,7 +95,7 @@ func cmdLights(br *ziggy.Bridge, args []string) error {
log.Info(). log.Info().
Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", l.ID).Str("type", l.ProductName). Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", l.ID).Str("type", l.ProductName).
Str("model", l.ModelID).Bool("on", l.IsOn()).Msgf("Light: %s", name) Str("model", l.ModelID).Bool("on", l.IsOn()).Msgf("Light: %s", name)
log.Trace().Msgf("%v", spew.Sprint(l)) log.Trace().Caller().Msgf("%v", spew.Sprint(l))
} }
return nil return nil
} }
@ -111,7 +111,7 @@ func cmdRules(br *ziggy.Bridge, args []string) error {
for _, r := range rules { for _, r := range rules {
log.Info().Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", r.ID). log.Info().Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", r.ID).
Str("status", r.Status).Msgf("Rule: %s", r.Name) Str("status", r.Status).Msgf("Rule: %s", r.Name)
log.Trace().Msgf("%v", spew.Sprint(r)) log.Trace().Caller().Msgf("%v", spew.Sprint(r))
} }
return nil return nil
} }
@ -127,7 +127,7 @@ func cmdSchedules(br *ziggy.Bridge, args []string) error {
for _, s := range schedules { for _, s := range schedules {
log.Info().Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", s.ID). log.Info().Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", s.ID).
Str("desc", s.Description).Msgf("Schedule: %s", s.Name) Str("desc", s.Description).Msgf("Schedule: %s", s.Name)
log.Trace().Msgf("%v", spew.Sprint(s)) log.Trace().Caller().Msgf("%v", spew.Sprint(s))
} }
return nil return nil
} }
@ -143,7 +143,7 @@ func cmdSensors(br *ziggy.Bridge, args []string) error {
for _, s := range sensors { for _, s := range sensors {
log.Info().Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", s.ID). log.Info().Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", s.ID).
Str("type", s.Type).Msgf("Sensor: %s", s.Name) Str("type", s.Type).Msgf("Sensor: %s", s.Name)
log.Trace().Msgf("%v", spew.Sprint(s)) log.Trace().Caller().Msgf("%v", spew.Sprint(s))
} }
return nil return nil
} }
@ -166,7 +166,7 @@ func cmdGroups(br *ziggy.Bridge, args []string) error {
} }
log.Info().Msg("\t[" + strconv.Itoa(lght.ID) + "] " + lght.Name + " (" + lght.ProductName + ")") log.Info().Msg("\t[" + strconv.Itoa(lght.ID) + "] " + lght.Name + " (" + lght.ProductName + ")")
} }
log.Trace().Msgf("%v", spew.Sprint(g)) log.Trace().Caller().Msgf("%v", spew.Sprint(g))
} }
return nil return nil
} }
@ -252,7 +252,7 @@ func cmdCp(br *ziggy.Bridge, args []string) error {
return err return err
} }
log.Info().Msgf("updated group %s to include light %s", targetGroup.Name, targetLight.Name) log.Info().Msgf("updated group %s to include light %s", targetGroup.Name, targetLight.Name)
log.Trace().Msgf("%v", spew.Sprint(resp)) log.Trace().Caller().Msgf("%v", spew.Sprint(resp))
return nil return nil
} }
@ -373,6 +373,17 @@ func cmdDump(br *ziggy.Bridge, args []string) error {
return nil return nil
} }
func cmdGetFullState(br *ziggy.Bridge, args []string) error {
var err error
var fullstate map[string]interface{}
fullstate, err = br.GetFullState()
if err != nil {
return err
}
spew.Dump(fullstate)
return nil
}
// cmdLoad imports a target JSON object and attempts to apply it to an existing object // cmdLoad imports a target JSON object and attempts to apply it to an existing object
func cmdLoad(br *ziggy.Bridge, args []string) error { func cmdLoad(br *ziggy.Bridge, args []string) error {
var js []byte var js []byte
@ -490,7 +501,7 @@ func cmdAdopt(br *ziggy.Bridge, args []string) error {
} }
for _, l := range newLights.Lights { for _, l := range newLights.Lights {
log.Info().Msgf("[+] %s", l) log.Info().Msgf("[+] %s", l)
log.Trace().Msgf("%v", spew.Sprint(l)) log.Trace().Caller().Msgf("%v", spew.Sprint(l))
} }
return nil return nil
} }

@ -1,12 +1,16 @@
package cli package cli
import ( import (
"fmt"
"strings" "strings"
"sync" "sync"
"time"
cli "git.tcp.direct/Mirrors/go-prompt" cli "git.tcp.direct/Mirrors/go-prompt"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/google/shlex" "github.com/google/shlex"
"git.tcp.direct/kayos/ziggs/internal/ziggy"
) )
const ( const (
@ -81,7 +85,7 @@ func (c completion) qualifies(line string) bool {
var ( var (
suggestions map[int]map[string]*completion suggestions map[int]map[string]*completion
suggestionMutex = &sync.RWMutex{} SuggestionMutex = &sync.RWMutex{}
) )
func init() { func init() {
@ -111,11 +115,23 @@ func init() {
Commands["info"] = newZiggsCommand(cmdInfo, "show information about a bridge", 0, "uname") Commands["info"] = newZiggsCommand(cmdInfo, "show information about a bridge", 0, "uname")
initCompletion() initCompletion()
Commands["reboot"] = newZiggsCommand(cmdReboot, "reboot bridge", 0) Commands["reboot"] = newZiggsCommand(cmdReboot, "reboot bridge", 0)
Commands["get-full-state"] = newZiggsCommand(cmdGetFullState, "get full state from bridge", 0)
Commands["sleep"] = newZiggsCommand(func(br *ziggy.Bridge, args []string) error {
if len(args) < 1 {
return fmt.Errorf("sleep requires 1 argument")
}
d, err := time.ParseDuration(args[0])
if err != nil {
return err
}
time.Sleep(d)
return nil
}, "sleep for a specified number of seconds", 1)
} }
func initCompletion() { func initCompletion() {
suggestionMutex.Lock() SuggestionMutex.Lock()
defer suggestionMutex.Unlock() defer SuggestionMutex.Unlock()
suggestions = make(map[int]map[string]*completion) suggestions = make(map[int]map[string]*completion)
suggestions[0] = make(map[string]*completion) suggestions[0] = make(map[string]*completion)
@ -198,8 +214,8 @@ func completer(in cli.Document) []cli.Suggest {
log.Trace().Int("head", head).Msgf("completing %v", infields) log.Trace().Int("head", head).Msgf("completing %v", infields)
} }
var sugs []cli.Suggest var sugs []cli.Suggest
suggestionMutex.RLock() SuggestionMutex.RLock()
defer suggestionMutex.RUnlock() defer SuggestionMutex.RUnlock()
for _, sug := range suggestions[head] { for _, sug := range suggestions[head] {
if !sug.qualifies(c) { if !sug.qualifies(c) {
continue continue

@ -5,8 +5,8 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/yunginnanet/huego"
"github.com/lucasb-eyer/go-colorful" "github.com/lucasb-eyer/go-colorful"
"github.com/yunginnanet/huego"
"git.tcp.direct/kayos/ziggs/internal/common" "git.tcp.direct/kayos/ziggs/internal/common"
"git.tcp.direct/kayos/ziggs/internal/system" "git.tcp.direct/kayos/ziggs/internal/system"
@ -65,7 +65,7 @@ func cpuInit(argVal string, bridge *ziggy.Bridge, cpuTarget cmdTarget) error {
continue continue
} }
cpuLastCol = clr.Hex() cpuLastCol = clr.Hex()
log.Trace().Msgf("CPU load color: %v", clr.Hex()) log.Trace().Caller().Msgf("CPU load color: %v", clr.Hex())
cHex, cErr := common.ParseHexColorFast(clr.Hex()) cHex, cErr := common.ParseHexColorFast(clr.Hex())
if cErr != nil { if cErr != nil {
log.Error().Err(cErr).Msg("failed to parse color") log.Error().Err(cErr).Msg("failed to parse color")
@ -87,7 +87,7 @@ func cpuInit(argVal string, bridge *ziggy.Bridge, cpuTarget cmdTarget) error {
} }
time.Sleep(750 * time.Millisecond) time.Sleep(750 * time.Millisecond)
cpuLastHue[head] = hue cpuLastHue[head] = hue
// log.Trace().Msgf("CPU load hue: %v", hue) // log.Trace().Caller().Msgf("CPU load hue: %v", hue)
target := lights[head] target := lights[head]
newh := 65000 - hue newh := 65000 - hue
if newh < 1 { if newh < 1 {

@ -23,6 +23,7 @@ func cmdCreate(br *ziggy.Bridge, args []string) error {
class = "" class = ""
) )
log.Debug().Msgf("creating group: %s", name) log.Debug().Msgf("creating group: %s", name)
for i, arg := range args { for i, arg := range args {
switch arg { switch arg {
case "group", name: case "group", name:
@ -34,19 +35,37 @@ func cmdCreate(br *ziggy.Bridge, args []string) error {
log.Debug().Msgf("group class: %s", class) log.Debug().Msgf("group class: %s", class)
continue continue
} }
var seenMap = make(map[string]bool)
if strings.Contains(arg, ",") { if strings.Contains(arg, ",") {
log.Debug().Msgf("found comma in arg %d, splitting argument by commas and remaking arg list", i) log.Debug().Msgf("found comma in arg %d, splitting argument by commas and remaking arg list", i)
args = append(args[:i], strings.Split(arg, ",")...) var newIDs []string
log.Debug().Msgf("new args: %v", args) newIDs = append(newIDs, strings.Split(arg, ",")...)
log.Debug().Msgf("new args: %v", newIDs)
for _, newArg := range newIDs {
if _, err := strconv.Atoi(newArg); err != nil {
return err
}
if seenMap[newArg] {
continue
}
seenMap[newArg] = true
ids = append(ids, newArg)
}
continue continue
} }
if seenMap[arg] {
continue
}
seenMap[arg] = true
_, err := strconv.Atoi(arg) _, err := strconv.Atoi(arg)
if err != nil { if err != nil {
return err return err
} }
log.Trace().Caller().Msgf("found light id: %s", arg)
ids = append(ids, arg) ids = append(ids, arg)
} }
log.Debug().Msgf("light ids: %+s", ids)
resp, err := br.CreateGroup(huego.Group{Name: name, Lights: ids, Type: groupType, Class: class}) resp, err := br.CreateGroup(huego.Group{Name: name, Lights: ids, Type: groupType, Class: class})
if err != nil { if err != nil {
return err return err

@ -10,8 +10,8 @@ import (
var tabber = tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', tabwriter.AlignRight) var tabber = tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', tabwriter.AlignRight)
func getHelp(target string) { func getHelp(target string) {
suggestionMutex.RLock() SuggestionMutex.RLock()
defer suggestionMutex.RUnlock() defer SuggestionMutex.RUnlock()
if target != "" && target != "meta" { if target != "" && target != "meta" {
for _, su := range suggestions[0] { for _, su := range suggestions[0] {
@ -31,7 +31,7 @@ func getHelp(target string) {
if su.inner.isAlias || su.isAlias { if su.inner.isAlias || su.isAlias {
su.isAlias = true su.isAlias = true
if extraDebug { if extraDebug {
log.Trace().Msgf("alias: %s", su.Text) log.Trace().Caller().Msgf("alias: %s", su.Text)
} }
} }
} }

@ -3,16 +3,24 @@ package cli
import ( import (
cli "git.tcp.direct/Mirrors/go-prompt" cli "git.tcp.direct/Mirrors/go-prompt"
"git.tcp.direct/kayos/ziggs/internal/config"
"git.tcp.direct/kayos/ziggs/internal/ziggy" "git.tcp.direct/kayos/ziggs/internal/ziggy"
) )
func processGroups(grps map[string]*ziggy.HueGroup) { func init() {
go func() {
log = config.GetLogger()
}()
}
func ProcessGroups(grps map[string]*ziggy.HueGroup) {
for grp, g := range grps { for grp, g := range grps {
log.Trace().Caller().Msgf("Processing group %s", grp)
suffix := "" suffix := ""
if g.Type != "" { if g.Type != "" {
suffix = " (" + g.Type + ")" suffix = " (" + g.Type + ")"
} }
suggestionMutex.Lock() SuggestionMutex.Lock()
suggestions[2][grp] = &completion{ suggestions[2][grp] = &completion{
Suggest: cli.Suggest{ Suggest: cli.Suggest{
Text: grp, Text: grp,
@ -24,17 +32,18 @@ func processGroups(grps map[string]*ziggy.HueGroup) {
}, },
root: false, root: false,
} }
suggestionMutex.Unlock() SuggestionMutex.Unlock()
} }
} }
func processScenes(scns map[string]*ziggy.HueScene) { func ProcessScenes(scns map[string]*ziggy.HueScene) {
for scn, s := range scns { for scn, s := range scns {
log.Trace().Caller().Msgf("Processing scene %s", scn)
suffix := "" suffix := ""
if s.Type != "" { if s.Type != "" {
suffix = " (" + s.Type + ")" suffix = " (" + s.Type + ")"
} }
suggestionMutex.Lock() SuggestionMutex.Lock()
suggestions[4][scn] = &completion{ suggestions[4][scn] = &completion{
Suggest: cli.Suggest{ Suggest: cli.Suggest{
Text: scn, Text: scn,
@ -47,7 +56,7 @@ func processScenes(scns map[string]*ziggy.HueScene) {
}, },
callback: func(args []string) bool { callback: func(args []string) bool {
if extraDebug { if extraDebug {
log.Trace().Msgf("Checking if scene %s belongs to group %s, their group is %s", log.Trace().Caller().Msgf("Checking if scene %s belongs to group %s, their group is %s",
s.Name, args[3], s.Group) s.Name, args[3], s.Group)
} }
if len(args) < 4 { if len(args) < 4 {
@ -61,7 +70,7 @@ func processScenes(scns map[string]*ziggy.HueScene) {
return false return false
case args[1] == "group" || args[1] == "g": case args[1] == "group" || args[1] == "g":
if extraDebug { if extraDebug {
log.Trace().Msgf("Checking if group %s is %s", args[3], s.Group) log.Trace().Caller().Msgf("Checking if group %s is %s", args[3], s.Group)
} }
if args[3] == s.Group { if args[3] == s.Group {
return true return true
@ -73,17 +82,18 @@ func processScenes(scns map[string]*ziggy.HueScene) {
}, },
root: false, root: false,
} }
suggestionMutex.Unlock() SuggestionMutex.Unlock()
} }
} }
func processLights(lghts map[string]*ziggy.HueLight) { func ProcessLights(lghts map[string]*ziggy.HueLight) {
for lt, l := range lghts { for lt, l := range lghts {
log.Trace().Caller().Msgf("Processing light %s", lt)
suffix := "" suffix := ""
if l.Type != "" { if l.Type != "" {
suffix = " (" + l.Type + ")" suffix = " (" + l.Type + ")"
} }
suggestionMutex.Lock() SuggestionMutex.Lock()
suggestions[2][lt] = &completion{ suggestions[2][lt] = &completion{
Suggest: cli.Suggest{ Suggest: cli.Suggest{
Text: lt, Text: lt,
@ -95,13 +105,14 @@ func processLights(lghts map[string]*ziggy.HueLight) {
}, },
root: false, root: false,
} }
suggestionMutex.Unlock() SuggestionMutex.Unlock()
} }
} }
func processBridges() { func ProcessBridges() {
for brd, b := range ziggy.Lucifer.Bridges { for brd, b := range ziggy.Lucifer.Bridges {
suggestionMutex.Lock() log.Trace().Caller().Msgf("Processing bridge %s", brd)
SuggestionMutex.Lock()
suggestions[1]["bridge"] = &completion{ suggestions[1]["bridge"] = &completion{
Suggest: cli.Suggest{ Suggest: cli.Suggest{
Text: brd, Text: brd,
@ -110,6 +121,6 @@ func processBridges() {
requires: map[int]map[string]bool{0: {"use": true, "u": true}}, requires: map[int]map[string]bool{0: {"use": true, "u": true}},
root: false, root: false,
} }
suggestionMutex.Unlock() SuggestionMutex.Unlock()
} }
} }

@ -264,9 +264,9 @@ func cmdSet(bridge *ziggy.Bridge, args []string) error {
default: default:
return errors.New("unknown target") return errors.New("unknown target")
} }
log.Trace().Msgf("current state: %v", currentState) log.Trace().Caller().Msgf("current state: %v", currentState)
for d, act := range actions { for d, act := range actions {
log.Trace().Msgf("running action %d", d) log.Trace().Caller().Msgf("running action %d", d)
err := act() err := act()
if err != nil { if err != nil {
return err return err
@ -277,7 +277,7 @@ func cmdSet(bridge *ziggy.Bridge, args []string) error {
case tlok: case tlok:
currentState = tl.State currentState = tl.State
} }
log.Trace().Msgf("new state: %v", currentState) log.Trace().Caller().Msgf("new state: %v", currentState)
} }
return nil return nil
} }

@ -5,8 +5,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/yunginnanet/huego"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/yunginnanet/huego"
"git.tcp.direct/kayos/ziggs/internal/ziggy" "git.tcp.direct/kayos/ziggs/internal/ziggy"
) )
@ -44,7 +44,7 @@ func cmdFirmwareUpdate(br *ziggy.Bridge, args []string) error {
if err != nil { if err != nil {
return err return err
} }
log.Trace().Msgf("new bridge update state:\n%s", spew.Sdump(cNew.SwUpdate2)) log.Trace().Caller().Msgf("new bridge update state:\n%s", spew.Sdump(cNew.SwUpdate2))
log.Info().Msgf("New software version: %s", c.SwVersion) log.Info().Msgf("New software version: %s", c.SwVersion)
log.Info().Msgf("New update state: %v", c.SwUpdate2.State) log.Info().Msgf("New update state: %v", c.SwUpdate2.State)
return err return err

@ -92,7 +92,7 @@ func setDefaults() {
Opt := make(map[string]map[string]interface{}) Opt := make(map[string]map[string]interface{})
Opt["logger"] = map[string]interface{}{ Opt["logger"] = map[string]interface{}{
"debug": true, "debug": true,
"trace": true, "trace": false,
"directory": deflogdir, "directory": deflogdir,
"nocolor": defNoColor, "nocolor": defNoColor,
"use_date_filename": true, "use_date_filename": true,

@ -11,9 +11,9 @@ import (
"sync" "sync"
"git.tcp.direct/kayos/common/entropy" "git.tcp.direct/kayos/common/entropy"
"github.com/yunginnanet/huego"
tui "github.com/manifoldco/promptui" tui "github.com/manifoldco/promptui"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/yunginnanet/huego"
"golang.org/x/net/proxy" "golang.org/x/net/proxy"
"git.tcp.direct/kayos/ziggs/internal/common" "git.tcp.direct/kayos/ziggs/internal/common"
@ -148,7 +148,7 @@ func GetControllers(bridges []config.KnownBridge) (br []*Bridge) {
log.Error().Str("caller", lightConfig.Hostname).Err(err).Msg("unsuccessful connection") log.Error().Str("caller", lightConfig.Hostname).Err(err).Msg("unsuccessful connection")
continue continue
} }
c.Log().Info().Str("caller", strings.Split(lightConfig.Hostname, "http://")[1]).Msg("connected") c.Log().Debug().Str("caller", strings.Split(lightConfig.Hostname, "http://")[1]).Msg("connected")
br = append(br, c) br = append(br, c)
} }
return return

@ -12,8 +12,8 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/yunginnanet/huego"
tui "github.com/manifoldco/promptui" tui "github.com/manifoldco/promptui"
"github.com/yunginnanet/huego"
"go4.org/netipx" "go4.org/netipx"
"git.tcp.direct/kayos/ziggs/internal/common" "git.tcp.direct/kayos/ziggs/internal/common"
@ -110,7 +110,7 @@ func checkAddrs(ctx context.Context, addrs []net.Addr, working *int32, resChan c
var init = &sync.Once{} var init = &sync.Once{}
log.Trace().Msg("checking addresses") log.Trace().Msg("checking addresses")
for _, a := range addrs { for _, a := range addrs {
log.Trace().Msgf("checking %s", a.String()) log.Trace().Caller().Msgf("checking %s", a.String())
rng := netipx.MustParseIPRange(a.String()) rng := netipx.MustParseIPRange(a.String())
for ipa := rng.From(); ipa != rng.To(); ipa = ipa.Next() { for ipa := rng.From(); ipa != rng.To(); ipa = ipa.Next() {
init.Do(func() { resChan <- &huego.Bridge{} }) init.Do(func() { resChan <- &huego.Bridge{} })
@ -127,7 +127,7 @@ func checkAddrs(ctx context.Context, addrs []net.Addr, working *int32, resChan c
break ctxLoop break ctxLoop
} }
} }
log.Trace().Msgf("checking %s", ipa.String()) log.Trace().Caller().Msgf("checking %s", ipa.String())
atomic.AddInt32(working, 1) atomic.AddInt32(working, 1)
go func(ip netip.Addr) { go func(ip netip.Addr) {
resChan <- enumerateBridge(ip, ctx) resChan <- enumerateBridge(ip, ctx)

23
main.go

@ -10,9 +10,9 @@ import (
"time" "time"
"git.tcp.direct/kayos/common/squish" "git.tcp.direct/kayos/common/squish"
"github.com/yunginnanet/huego"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/yunginnanet/huego"
"git.tcp.direct/kayos/ziggs/internal/cli" "git.tcp.direct/kayos/ziggs/internal/cli"
"git.tcp.direct/kayos/ziggs/internal/common" "git.tcp.direct/kayos/ziggs/internal/common"
@ -43,8 +43,8 @@ func aesthetic() {
if len(Version) > 18 { if len(Version) > 18 {
index = 18 index = 18
} }
log.Info().Str("version", Version[:index]).Send() log.Debug().Str("version", Version[:index]).Send()
log.Info().Str("built", compileTime).Send() log.Debug().Str("built", compileTime).Send()
} }
func init() { func init() {
@ -151,6 +151,14 @@ func main() {
log.Fatal().Err(err).Msg("failed to get bridges") log.Fatal().Err(err).Msg("failed to get bridges")
} }
log = config.GetLogger()
cli.ProcessBridges()
go func() {
cli.ProcessGroups(ziggy.GetGroupMap())
cli.ProcessLights(ziggy.GetLightMap())
cli.ProcessScenes(ziggy.GetSceneMap())
}()
data.Start() data.Start()
defer data.Close() defer data.Close()
@ -158,7 +166,7 @@ func main() {
cli.StartCLI() cli.StartCLI()
} }
for _, arg := range os.Args { for i, arg := range os.Args {
switch arg { switch arg {
case "events": case "events":
evch := make(chan string, 10) evch := make(chan string, 10)
@ -171,6 +179,7 @@ func main() {
os.Stdout.WriteString("\n") os.Stdout.WriteString("\n")
} }
}() }()
evc := haptic.NewEventClient() evc := haptic.NewEventClient()
evc.Subscribe("*", evch) evc.Subscribe("*", evch)
if err = evc.Start(config.KnownBridges[0].Hostname, config.KnownBridges[0].Username); err != nil && if err = evc.Start(config.KnownBridges[0].Hostname, config.KnownBridges[0].Username); err != nil &&
@ -194,8 +203,7 @@ func main() {
log.Debug().Msg("executing " + arg) log.Debug().Msg("executing " + arg)
if len(os.Args) < 2 { if len(os.Args) < 2 {
for _, k := range Known { for _, k := range Known {
ctx := context.TODO() FindLights(context.TODO(), k)
FindLights(ctx, k)
} }
} }
case "shell": case "shell":
@ -212,6 +220,9 @@ func main() {
sensptr = append(sensptr, &s) sensptr = append(sensptr, &s)
} }
selSensor(sensptr) selSensor(sensptr)
case "--":
log.Debug().Msg("executing " + strings.Join(os.Args[i+1:], " "))
cli.Executor(strings.Join(os.Args[i+1:], " "))
} }
} }