This commit is contained in:
kayos@tcp.direct 2022-07-08 17:09:49 -07:00
parent e0c7f1dbf4
commit 2705ff6140
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
7 changed files with 92 additions and 74 deletions

3
common/ui.go Normal file

@ -0,0 +1,3 @@
package common
var ZiggsPointer = func(to []rune) []rune { return []rune(``) }

@ -97,9 +97,9 @@ func setDefaults() {
"use_date_filename": true, "use_date_filename": true,
} }
Opt["bridges"] = map[string]interface{}{ Opt["bridges"] = map[string]interface{}{
"hostname": "192.168.6.100", "hostname": "192.168.69.100",
"username": "", "username": "",
"proxy": "socks5://127.0.0.1:8060", "proxy": "",
} }
Opt["http"] = map[string]interface{}{ Opt["http"] = map[string]interface{}{

@ -11,8 +11,9 @@ import (
tui "github.com/manifoldco/promptui" tui "github.com/manifoldco/promptui"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"git.tcp.direct/kayos/ziggs/common"
"git.tcp.direct/kayos/ziggs/config" "git.tcp.direct/kayos/ziggs/config"
"git.tcp.direct/kayos/ziggs/lights" "git.tcp.direct/kayos/ziggs/ziggy"
) )
var log *zerolog.Logger var log *zerolog.Logger
@ -45,10 +46,8 @@ func InteractiveAuth() string {
// 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) {
cmd = strings.TrimSpace(cmd) cmd = strings.TrimSpace(cmd)
var args []string var args []string
args = strings.Split(cmd, " ") args = strings.Split(cmd, " ")
switch args[0] { switch args[0] {
case "quit", "exit": case "quit", "exit":
os.Exit(0) os.Exit(0)
@ -57,7 +56,7 @@ func executor(cmd string) {
println("use: use <bridge>") println("use: use <bridge>")
return return
} }
if br, ok := lights.Lucifer.Bridges[args[1]]; !ok { if br, ok := ziggy.Lucifer.Bridges[args[1]]; !ok {
println("invalid bridge: " + args[1]) println("invalid bridge: " + args[1])
} else { } else {
selectedBridge = args[1] selectedBridge = args[1]
@ -83,12 +82,20 @@ func executor(cmd string) {
default: default:
bcmd, ok := bridgeCMD[args[0]] bcmd, ok := bridgeCMD[args[0]]
if !ok { if !ok {
println()
return return
} }
br, ok := lights.Lucifer.Bridges[selectedBridge] br, ok := ziggy.Lucifer.Bridges[selectedBridge]
if selectedBridge == "" || !ok { if selectedBridge == "" || !ok {
for _, br := range lights.Lucifer.Bridges { prompt := tui.Select{
Label: "Send to all known bridges?",
Items: []string{"yes", "no"},
Pointer: common.ZiggsPointer,
}
_, ch, _ := prompt.Run()
if ch != "yes" {
return
}
for _, br := range ziggy.Lucifer.Bridges {
go bcmd(br, args[1:]) go bcmd(br, args[1:])
} }
} else { } else {
@ -98,7 +105,6 @@ func executor(cmd string) {
} }
} }
} }
} }
func getHelp(target string) { func getHelp(target string) {
@ -129,7 +135,7 @@ func getHelp(target string) {
*/ */
} }
func cmdScan(br *lights.Bridge, args []string) error { func cmdScan(br *ziggy.Bridge, args []string) error {
r, err := br.FindLights() r, err := br.FindLights()
if err != nil { if err != nil {
return err return err
@ -171,7 +177,7 @@ func StartCLI() {
log = config.GetLogger() log = config.GetLogger()
var hist []string var hist []string
processBridges(lights.Lucifer.Bridges) processBridges(ziggy.Lucifer.Bridges)
/* comphead := 0 /* comphead := 0
compmu := &sync.Mutex{} compmu := &sync.Mutex{}

@ -9,7 +9,7 @@ import (
cli "git.tcp.direct/Mirrors/go-prompt" cli "git.tcp.direct/Mirrors/go-prompt"
"github.com/amimof/huego" "github.com/amimof/huego"
"git.tcp.direct/kayos/ziggs/lights" "git.tcp.direct/kayos/ziggs/ziggy"
) )
var errInvalidFormat = errors.New("invalid format") var errInvalidFormat = errors.New("invalid format")
@ -49,7 +49,7 @@ func ParseHexColorFast(s string) (c color.RGBA, err error) {
return return
} }
func cmdLights(br *lights.Bridge, args []string) error { func cmdLights(br *ziggy.Bridge, args []string) error {
if len(br.HueLights) == 0 { if len(br.HueLights) == 0 {
return errors.New("no lights found") return errors.New("no lights found")
} }
@ -61,7 +61,7 @@ func cmdLights(br *lights.Bridge, args []string) error {
return nil return nil
} }
func cmdSet(bridge *lights.Bridge, args []string) error { func cmdSet(bridge *ziggy.Bridge, args []string) error {
if len(args) < 3 { if len(args) < 3 {
return errors.New("not enough arguments") return errors.New("not enough arguments")
} }
@ -80,7 +80,8 @@ func cmdSet(bridge *lights.Bridge, args []string) error {
var groupmap map[string]huego.Group var groupmap map[string]huego.Group
var action func() error type action func() error
var actions []action
var currentState *huego.State var currentState *huego.State
var argHead = -1 var argHead = -1
@ -107,11 +108,11 @@ func cmdSet(bridge *lights.Bridge, args []string) error {
} }
target = &g target = &g
case "on": case "on":
action = target.On actions = append(actions, target.On)
case "off": case "off":
action = target.Off actions = append(actions, target.Off)
case "brightness--", "dim": case "brightness--", "dim":
action = func() error { actions = append(actions, func() error {
if currentState == nil { if currentState == nil {
return fmt.Errorf("no state found") return fmt.Errorf("no state found")
} }
@ -120,9 +121,9 @@ func cmdSet(bridge *lights.Bridge, args []string) error {
err = fmt.Errorf("couldn't lower brightness: %w", err) err = fmt.Errorf("couldn't lower brightness: %w", err)
} }
return err return err
} })
case "brightness++", "brighten": case "brightness++", "brighten":
action = func() error { actions = append(actions, func() error {
if currentState == nil { if currentState == nil {
return fmt.Errorf("no state found") return fmt.Errorf("no state found")
} }
@ -131,7 +132,7 @@ func cmdSet(bridge *lights.Bridge, args []string) error {
err = fmt.Errorf("couldn't raise brightness: %w", err) err = fmt.Errorf("couldn't raise brightness: %w", err)
} }
return err return err
} })
case "brightness": case "brightness":
if len(args) == argHead-1 { if len(args) == argHead-1 {
return errors.New("no brightness specified") return errors.New("no brightness specified")
@ -141,13 +142,13 @@ func cmdSet(bridge *lights.Bridge, args []string) error {
if numErr != nil { if numErr != nil {
return fmt.Errorf("given brightness is not a number: %w", numErr) return fmt.Errorf("given brightness is not a number: %w", numErr)
} }
action = func() error { actions = append(actions, func() error {
err := target.Bri(uint8(newBrightness)) err := target.Bri(uint8(newBrightness))
if err != nil { if err != nil {
err = fmt.Errorf("failed to set brightness: %w", err) err = fmt.Errorf("failed to set brightness: %w", err)
} }
return err return err
} })
case "color": case "color":
if len(args) == argHead-1 { if len(args) == argHead-1 {
return errors.New("not enough arguments") return errors.New("not enough arguments")
@ -157,26 +158,26 @@ func cmdSet(bridge *lights.Bridge, args []string) error {
if err != nil { if err != nil {
return err return err
} }
action = func() error { actions = append(actions, func() error {
colErr := target.Col(newcolor) colErr := target.Col(newcolor)
if colErr != nil { if colErr != nil {
colErr = fmt.Errorf("failed to set color: %w", colErr) colErr = fmt.Errorf("failed to set color: %w", colErr)
} }
return colErr return colErr
} })
case "alert": case "alert":
action = func() error { actions = append(actions, func() error {
alErr := target.Alert("select") alErr := target.Alert("select")
if alErr != nil { if alErr != nil {
alErr = fmt.Errorf("failed to turn on alert: %w", alErr) alErr = fmt.Errorf("failed to turn on alert: %w", alErr)
} }
return alErr return alErr
} })
default: default:
return fmt.Errorf("unknown argument: " + args[argHead]) return fmt.Errorf("unknown argument: " + args[argHead])
} }
} }
if action == nil { if actions == nil {
return errors.New("no action specified") return errors.New("no action specified")
} }
if target == nil { if target == nil {
@ -191,21 +192,24 @@ func cmdSet(bridge *lights.Bridge, args []string) error {
currentState = tl.State currentState = tl.State
} }
log.Trace().Msgf("current state: %v", currentState) log.Trace().Msgf("current state: %v", currentState)
err := action() for d, act := range actions {
if err != nil { log.Trace().Msgf("running action %d", d)
return err err := act()
if err != nil {
return err
}
switch {
case tgok:
currentState = tg.State
case tlok:
currentState = tl.State
}
log.Trace().Msgf("new state: %v", currentState)
} }
switch {
case tgok:
currentState = tg.State
case tlok:
currentState = tl.State
}
log.Trace().Msgf("new state: %v", currentState)
return nil return nil
} }
func getGroupMap(br *lights.Bridge) (map[string]huego.Group, error) { func getGroupMap(br *ziggy.Bridge) (map[string]huego.Group, error) {
var groupmap = make(map[string]huego.Group) var groupmap = make(map[string]huego.Group)
gs, err := br.Bridge.GetGroups() gs, err := br.Bridge.GetGroups()
if err != nil { if err != nil {
@ -217,7 +221,7 @@ func getGroupMap(br *lights.Bridge) (map[string]huego.Group, error) {
return groupmap, nil return groupmap, nil
} }
func cmdGroups(br *lights.Bridge, args []string) error { func cmdGroups(br *ziggy.Bridge, args []string) error {
groupmap, err := getGroupMap(br) groupmap, err := getGroupMap(br)
if err != nil { if err != nil {
return err return err
@ -232,7 +236,7 @@ func cmdGroups(br *lights.Bridge, args []string) error {
return nil return nil
} }
type reactor func(bridge *lights.Bridge, args []string) error type reactor func(bridge *ziggy.Bridge, args []string) error
var bridgeCMD = map[string]reactor{ var bridgeCMD = map[string]reactor{
"scan": cmdScan, "scan": cmdScan,
@ -247,7 +251,7 @@ type completeMapper map[cli.Suggest][]cli.Suggest
var suggestions completeMapper = make(map[cli.Suggest][]cli.Suggest) var suggestions completeMapper = make(map[cli.Suggest][]cli.Suggest)
func processBridges(brs map[string]*lights.Bridge) { func processBridges(brs map[string]*ziggy.Bridge) {
for brd, c := range brs { for brd, c := range brs {
suggestions[cli.Suggest{Text: "use"}] = append( suggestions[cli.Suggest{Text: "use"}] = append(
suggestions[cli.Suggest{Text: "use"}], suggestions[cli.Suggest{Text: "use"}],

25
main.go

@ -10,9 +10,10 @@ import (
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"git.tcp.direct/kayos/ziggs/common"
"git.tcp.direct/kayos/ziggs/config" "git.tcp.direct/kayos/ziggs/config"
"git.tcp.direct/kayos/ziggs/interactive" "git.tcp.direct/kayos/ziggs/interactive"
"git.tcp.direct/kayos/ziggs/lights" "git.tcp.direct/kayos/ziggs/ziggy"
) )
var ( var (
@ -28,23 +29,23 @@ func init() {
} }
} }
func TurnAll(Known []*lights.Bridge, mode lights.ToggleMode) { func TurnAll(Known []*ziggy.Bridge, mode ziggy.ToggleMode) {
for _, bridge := range Known { for _, bridge := range Known {
for _, l := range bridge.HueLights { for _, l := range bridge.HueLights {
go func(l *lights.HueLight) { go func(l *ziggy.HueLight) {
l.Log().Debug(). l.Log().Debug().
Str("caller", bridge.Host). Str("caller", bridge.Host).
Str("type", l.ProductName). Str("type", l.ProductName).
Bool("on", l.IsOn()).Msg(l.ModelID) Bool("on", l.IsOn()).Msg(l.ModelID)
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
lights.Assert(ctx, l, mode) ziggy.Assert(ctx, l, mode)
defer cancel() defer cancel()
}(l) }(l)
} }
} }
} }
func FindLights(ctx context.Context, c *lights.Bridge) error { func FindLights(ctx context.Context, c *ziggy.Bridge) error {
log.Trace().Msg("looking for lights...") log.Trace().Msg("looking for lights...")
resp, err := c.FindLights() resp, err := c.FindLights()
if err != nil { if err != nil {
@ -71,7 +72,7 @@ func FindLights(ctx context.Context, c *lights.Bridge) error {
} }
} }
func getNewSensors(known *lights.Bridge) { func getNewSensors(known *ziggy.Bridge) {
go known.FindSensors() go known.FindSensors()
Sensors, err := known.GetNewSensors() Sensors, err := known.GetNewSensors()
if err != nil { if err != nil {
@ -98,7 +99,7 @@ func selSensor(Sensors []*huego.Sensor) huego.Sensor {
CursorPos: 0, CursorPos: 0,
HideHelp: false, HideHelp: false,
HideSelected: false, HideSelected: false,
Pointer: func(to []rune) []rune { return []rune(``) }, Pointer: common.ZiggsPointer,
} }
i, s, e := p.Run() i, s, e := p.Run()
if e != nil { if e != nil {
@ -109,9 +110,9 @@ func selSensor(Sensors []*huego.Sensor) huego.Sensor {
} }
func main() { func main() {
var Known []*lights.Bridge var Known []*ziggy.Bridge
var err error var err error
Known, err = lights.Setup() Known, err = ziggy.Setup()
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("failed to get bridges") log.Fatal().Err(err).Msg("failed to get bridges")
@ -123,13 +124,13 @@ func main() {
case "on": case "on":
log.Debug().Msg("turning all " + arg) log.Debug().Msg("turning all " + arg)
TurnAll(Known, lights.ToggleOn) TurnAll(Known, ziggy.ToggleOn)
case "off": case "off":
log.Debug().Msg("turning all " + arg) log.Debug().Msg("turning all " + arg)
TurnAll(Known, lights.ToggleOff) TurnAll(Known, ziggy.ToggleOff)
case "rainbow": case "rainbow":
log.Debug().Msg("turning all " + arg) log.Debug().Msg("turning all " + arg)
TurnAll(Known, lights.ToggleRainbow) TurnAll(Known, ziggy.ToggleRainbow)
case "scan": case "scan":
log.Debug().Msg("executing " + arg) log.Debug().Msg("executing " + arg)
if len(os.Args) < 2 { if len(os.Args) < 2 {

@ -1,4 +1,4 @@
package lights package ziggy
import ( import (
"context" "context"
@ -21,6 +21,7 @@ import (
"golang.org/x/net/proxy" "golang.org/x/net/proxy"
"inet.af/netaddr" "inet.af/netaddr"
"git.tcp.direct/kayos/ziggs/common"
"git.tcp.direct/kayos/ziggs/config" "git.tcp.direct/kayos/ziggs/config"
) )
@ -281,9 +282,7 @@ func promptForUser(cnt *Bridge) bool {
Items: []string{"Create new user", "Provide existing username"}, Items: []string{"Create new user", "Provide existing username"},
CursorPos: 0, CursorPos: 0,
IsVimMode: false, IsVimMode: false,
Pointer: func(x []rune) []rune { Pointer: common.ZiggsPointer,
return []rune("")
},
} }
choice, _, _ := confirmPrompt.Run() choice, _, _ := confirmPrompt.Run()
switch choice { switch choice {
@ -307,9 +306,7 @@ func promptForUser(cnt *Bridge) bool {
}, },
Mask: 'x', Mask: 'x',
HideEntered: false, HideEntered: false,
Pointer: func(x []rune) []rune { Pointer: common.ZiggsPointer,
return []rune("")
},
} }
var err error var err error
var input string var input string
@ -409,15 +406,13 @@ func scanChoicePrompt(interfaces []net.Interface) net.Interface {
Items: interfaces, Items: interfaces,
CursorPos: 0, CursorPos: 0,
IsVimMode: false, IsVimMode: false,
Pointer: func(x []rune) []rune { Pointer: common.ZiggsPointer,
return []rune("")
},
} }
choice, _, _ := confirmPrompt.Run() choice, _, _ := confirmPrompt.Run()
return interfaces[choice] return interfaces[choice]
} }
func checkAddrs(addrs []net.Addr, working *int32, resChan chan interface{}) { func checkAddrs(ctx context.Context, addrs []net.Addr, working *int32, resChan chan interface{}) {
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 {
@ -425,11 +420,18 @@ func checkAddrs(addrs []net.Addr, working *int32, resChan chan interface{}) {
ips := network.IterateNetRange(netaddr.MustParseIPPrefix(a.String())) ips := network.IterateNetRange(netaddr.MustParseIPPrefix(a.String()))
for ipa := range ips { for ipa := range ips {
init.Do(func() { resChan <- &huego.Bridge{} }) init.Do(func() { resChan <- &huego.Bridge{} })
ctxLoop:
for { for {
if atomic.LoadInt32(working) > 50 { select {
time.Sleep(time.Second) case <-ctx.Done():
return
default:
if atomic.LoadInt32(working) > 25 {
time.Sleep(100 * time.Millisecond)
continue
}
break ctxLoop
} }
break
} }
log.Trace().Msgf("checking %s", ipa.String()) log.Trace().Msgf("checking %s", ipa.String())
atomic.AddInt32(working, 1) atomic.AddInt32(working, 1)
@ -461,8 +463,9 @@ func scanForBridges() ([]*huego.Bridge, error) {
} }
var working int32 var working int32
resChan := make(chan interface{}, 55) resChan := make(chan interface{}, 55)
ctx, cancel := context.WithCancel(context.Background())
log.Trace().Interface("addresses", addrs).Msg("checkAddrs()") log.Trace().Interface("addresses", addrs).Msg("checkAddrs()")
go checkAddrs(addrs, &working, resChan) go checkAddrs(ctx, addrs, &working, resChan)
<-resChan // wait for sync.Once to throw us a nil <-resChan // wait for sync.Once to throw us a nil
resultLoop: resultLoop:
@ -473,9 +476,12 @@ resultLoop:
if ok && bridge != nil { if ok && bridge != nil {
log.Info().Msgf("found %T: %v", bridge, bridge) log.Info().Msgf("found %T: %v", bridge, bridge)
hueIPs = append(hueIPs, bridge) hueIPs = append(hueIPs, bridge)
cancel()
atomic.StoreInt32(&working, 0)
} }
default: default:
if atomic.LoadInt32(&working) <= 0 { if atomic.LoadInt32(&working) <= 0 {
cancel()
break resultLoop break resultLoop
} }
} }
@ -495,9 +501,7 @@ func promptForDiscovery() error {
Items: []string{"Yes", "No"}, Items: []string{"Yes", "No"},
CursorPos: 0, CursorPos: 0,
IsVimMode: false, IsVimMode: false,
Pointer: func(x []rune) []rune { Pointer: common.ZiggsPointer,
return []rune("")
},
} }
choice, _, _ := confirmPrompt.Run() choice, _, _ := confirmPrompt.Run()
if choice != 0 { if choice != 0 {

@ -1,4 +1,4 @@
package lights package ziggy
// Multiplex is all of the lights (all of the lights). // Multiplex is all of the lights (all of the lights).
// I'll see myself out. // I'll see myself out.