arson on a massive scale
This commit is contained in:
		
						commit
						5233111f25
					
				| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/rileys-trash-can/newtecrs82obs"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	if len(os.Args) < 2 {
 | 
			
		||||
		log.Fatalf("Usage: log <serial>")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	port := os.Args[1]
 | 
			
		||||
	conn, err := nt8.Open(port)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Failed to connect: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn.InitBlynk()
 | 
			
		||||
 | 
			
		||||
	ch := conn.ReadCh()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		e := <-ch
 | 
			
		||||
 | 
			
		||||
		switch event := e.(type) {
 | 
			
		||||
		case *nt8.EventButton:
 | 
			
		||||
			log.Printf("%T %#v", event, event)
 | 
			
		||||
 | 
			
		||||
		case *nt8.EventSlider:
 | 
			
		||||
			log.Printf("%X %3d", event.Type, event.Value)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
serial.port: "/dev/ttyUSB0"
 | 
			
		||||
 | 
			
		||||
boolshit: true
 | 
			
		||||
 | 
			
		||||
obs.addr: "10.10.42.160:4444"
 | 
			
		||||
obs.pass: "asdfgh"
 | 
			
		||||
 | 
			
		||||
trans.auto: "Fade"
 | 
			
		||||
trans.take: "Cut"
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,343 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	obs "github.com/andreykaipov/goobs"
 | 
			
		||||
	obse "github.com/andreykaipov/goobs/api/events"
 | 
			
		||||
	obss "github.com/andreykaipov/goobs/api/events/subscriptions"
 | 
			
		||||
	obssc "github.com/andreykaipov/goobs/api/requests/scenes"
 | 
			
		||||
	obst "github.com/andreykaipov/goobs/api/requests/transitions"
 | 
			
		||||
	"github.com/rileys-trash-can/newtecrs82obs"
 | 
			
		||||
	"gopkg.in/yaml.v3"
 | 
			
		||||
	"log"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Config config
 | 
			
		||||
 | 
			
		||||
type config struct {
 | 
			
		||||
	Serial string `yaml:"serial.port"`
 | 
			
		||||
 | 
			
		||||
	Boolshit bool `yaml:"boolshit"` // enables disables bullshit
 | 
			
		||||
 | 
			
		||||
	OBSAddr string `yaml:"obs.addr"` // ip with port
 | 
			
		||||
	OBSPass string `yaml:"obs.pass"` // password
 | 
			
		||||
 | 
			
		||||
	TransAuto string `yaml:"trans.auto"` // transition used for auto button
 | 
			
		||||
	TransTake string `yaml:"trans.take"` // transition used for take button
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readconfig() {
 | 
			
		||||
	f, err := os.OpenFile("config.yml", os.O_RDONLY, 0755)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Failed to open config: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
	dec := yaml.NewDecoder(f)
 | 
			
		||||
	err = dec.Decode(&Config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Failed to decode config: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	log.SetFlags(log.Flags() | log.Lshortfile)
 | 
			
		||||
 | 
			
		||||
	readconfig()
 | 
			
		||||
 | 
			
		||||
	port := Config.Serial
 | 
			
		||||
	conn, err := nt8.Open(port)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Failed to connect: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// conn.InitBlynk()
 | 
			
		||||
 | 
			
		||||
	ch := conn.ReadCh()
 | 
			
		||||
 | 
			
		||||
	obsc, err := obs.New(Config.OBSAddr,
 | 
			
		||||
		obs.WithPassword(Config.OBSPass),
 | 
			
		||||
		obs.WithEventSubscriptions(obss.Scenes),
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Failed to connect: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t, err := obsc.Transitions.GetTransitionKindList()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Failed to connect gtkl: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, name := range t.TransitionKinds {
 | 
			
		||||
		log.Printf(" > %s", name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sl, err := obsc.Scenes.GetSceneList()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("Failed to gsl: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sceneindex := make([]string, 8)
 | 
			
		||||
	scenenamemap := make(map[string]int)
 | 
			
		||||
 | 
			
		||||
	lim := lower(len(sl.Scenes), 8)
 | 
			
		||||
	for i := 0; i < lim; i++ {
 | 
			
		||||
		sceneindex[i] = sl.Scenes[lim-i-1].SceneName
 | 
			
		||||
		scenenamemap[sl.Scenes[lim-i-1].SceneName] = i
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range sceneindex {
 | 
			
		||||
		log.Printf("%d %v", k, v)
 | 
			
		||||
	}
 | 
			
		||||
	log.Printf("scenemap: %+#v", scenenamemap)
 | 
			
		||||
 | 
			
		||||
	const (
 | 
			
		||||
		TBAR uint8 = iota
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	ticker := time.NewTicker(time.Second / 10)
 | 
			
		||||
	changed := make(map[uint8]struct{})
 | 
			
		||||
	debounce := make(map[uint16]time.Time)
 | 
			
		||||
	var tbarbos float64
 | 
			
		||||
 | 
			
		||||
	deb := func(t uint16) bool {
 | 
			
		||||
		now := time.Now()
 | 
			
		||||
 | 
			
		||||
		last, ok := debounce[t]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			debounce[t] = now
 | 
			
		||||
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if last.Before(now.Add(-time.Millisecond * 100)) {
 | 
			
		||||
			debounce[t] = now
 | 
			
		||||
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go obsc.Listen(func(l any) {
 | 
			
		||||
		switch event := l.(type) {
 | 
			
		||||
		case *obse.CurrentPreviewSceneChanged:
 | 
			
		||||
			log.Printf("current preview scene: %s %d", event.SceneName, scenenamemap[event.SceneName]+1)
 | 
			
		||||
			program := make([]nt8.CmdLight, 0)
 | 
			
		||||
 | 
			
		||||
			for i := uint8(0); i < 8; i++ {
 | 
			
		||||
				program = append(program, nt8.CmdLight{
 | 
			
		||||
					Type:  nt8.ButtonPreview,
 | 
			
		||||
					Value: i,
 | 
			
		||||
					State: nt8.LightOff,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			program[7-scenenamemap[event.SceneName]].State = nt8.LightOn
 | 
			
		||||
 | 
			
		||||
			conn.LightCmdCh <- program
 | 
			
		||||
			break
 | 
			
		||||
 | 
			
		||||
		case *obse.CurrentProgramSceneChanged:
 | 
			
		||||
			log.Printf("current program scene: %s %d", event.SceneName, scenenamemap[event.SceneName]+1)
 | 
			
		||||
			program := make([]nt8.CmdLight, 0)
 | 
			
		||||
 | 
			
		||||
			for i := uint8(0); i < 8; i++ {
 | 
			
		||||
				program = append(program, nt8.CmdLight{
 | 
			
		||||
					Type:  nt8.ButtonProgram,
 | 
			
		||||
					Value: i,
 | 
			
		||||
					State: nt8.LightOff,
 | 
			
		||||
				})
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			program[7-scenenamemap[event.SceneName]].State = nt8.LightOn
 | 
			
		||||
 | 
			
		||||
			conn.LightCmdCh <- program
 | 
			
		||||
			break
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			//log.Printf("non handled thingy %T", event)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-ticker.C:
 | 
			
		||||
			if _, ok := changed[TBAR]; ok {
 | 
			
		||||
				_, err = obsc.Transitions.SetTBarPosition(&obst.SetTBarPositionParams{
 | 
			
		||||
					Position: i(tbarbos),
 | 
			
		||||
					Release:  i(tbarbos == 0 || tbarbos == 1),
 | 
			
		||||
				})
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Printf("Error setting tbar: %s", err)
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			changed = make(map[uint8]struct{})
 | 
			
		||||
 | 
			
		||||
		case e := <-ch:
 | 
			
		||||
			switch event := e.(type) {
 | 
			
		||||
			case *nt8.EventButton:
 | 
			
		||||
				if event.Direction != 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				switch event.Type {
 | 
			
		||||
				case nt8.ButtonProgram:
 | 
			
		||||
					log.Printf("Setting Program to %d", 7-event.Value)
 | 
			
		||||
					obsc.Scenes.SetCurrentProgramScene(&obssc.SetCurrentProgramSceneParams{
 | 
			
		||||
						SceneName: &sceneindex[7-event.Value],
 | 
			
		||||
					})
 | 
			
		||||
 | 
			
		||||
					break
 | 
			
		||||
				case nt8.ButtonPreview:
 | 
			
		||||
					log.Printf("Setting Program to %d", 7-event.Value)
 | 
			
		||||
					obsc.Scenes.SetCurrentPreviewScene(&obssc.SetCurrentPreviewSceneParams{
 | 
			
		||||
						SceneName: &sceneindex[7-event.Value],
 | 
			
		||||
					})
 | 
			
		||||
 | 
			
		||||
					break
 | 
			
		||||
				case nt8.ButtonAutoTakeDSK:
 | 
			
		||||
					switch event.Value {
 | 
			
		||||
					case 0x01: // TAKE or 0x02
 | 
			
		||||
						if deb(nt8.ButtonAutoTakeDSK<<8 | 0x01) {
 | 
			
		||||
							continue
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						go func() {
 | 
			
		||||
							oldprog, err := obsc.Scenes.GetCurrentProgramScene()
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								log.Printf("oldprogram: %s (uuid %s)", oldprog.SceneName, oldprog.SceneUuid)
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							_, err = obsc.Transitions.SetCurrentSceneTransition(&obst.SetCurrentSceneTransitionParams{
 | 
			
		||||
								TransitionName: &Config.TransTake,
 | 
			
		||||
							})
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								log.Printf("set transition: %s", err)
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							_, err = obsc.Transitions.TriggerStudioModeTransition()
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								log.Printf("Studiomodetransition: %s", err)
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							_, err = obsc.Scenes.SetCurrentPreviewScene(&obssc.SetCurrentPreviewSceneParams{
 | 
			
		||||
								SceneUuid: &oldprog.SceneUuid,
 | 
			
		||||
							})
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								log.Printf("set current preview: %s", err)
 | 
			
		||||
							}
 | 
			
		||||
						}()
 | 
			
		||||
					case 0x03: // AUTO
 | 
			
		||||
 | 
			
		||||
						go func() {
 | 
			
		||||
							oldprog, err := obsc.Scenes.GetCurrentProgramScene()
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								log.Printf("oldprogram: %s (uuid %s)", oldprog.SceneName, oldprog.SceneUuid)
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							_, err = obsc.Transitions.SetCurrentSceneTransition(&obst.SetCurrentSceneTransitionParams{
 | 
			
		||||
								TransitionName: &Config.TransAuto,
 | 
			
		||||
							})
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								log.Printf("set transition: %s", err)
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							_, err = obsc.Transitions.TriggerStudioModeTransition()
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								log.Printf("Studiomodetransition: %s", err)
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							_, err = obsc.Scenes.SetCurrentPreviewScene(&obssc.SetCurrentPreviewSceneParams{
 | 
			
		||||
								SceneUuid: &oldprog.SceneUuid,
 | 
			
		||||
							})
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								log.Printf("set current preview: %s", err)
 | 
			
		||||
							}
 | 
			
		||||
						}()
 | 
			
		||||
						if Config.Boolshit {
 | 
			
		||||
							go exec.Command("gti").Run()
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
					case 0x05: // DDR
 | 
			
		||||
						if Config.Boolshit {
 | 
			
		||||
							println(" ____  ____  ____\n|  _ \\|  _ \\|  _ \\\n| | | | | | | |_) |\n| |_| | |_| |  _ <\n|____/|____/|_| \\_\\")
 | 
			
		||||
							println(ddrquote())
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				break
 | 
			
		||||
 | 
			
		||||
			case *nt8.EventSlider:
 | 
			
		||||
				if event.Type == nt8.SliderTbar {
 | 
			
		||||
					value := (float64(event.Value)) / 250
 | 
			
		||||
					if value > 1 {
 | 
			
		||||
						value = 1
 | 
			
		||||
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if value < 0 {
 | 
			
		||||
						value = 0
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					log.Printf("tbar %1.3f", value)
 | 
			
		||||
 | 
			
		||||
					tbarbos = value
 | 
			
		||||
					changed[TBAR] = struct{}{}
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				log.Printf("%X %3d", event.Type, event.Value)
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	obsc.Disconnect()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func i[K any](a K) *K {
 | 
			
		||||
	return &a
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func lower(a, b int) int {
 | 
			
		||||
	if a > b {
 | 
			
		||||
		return b
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return a
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var ddrquotes = []string{
 | 
			
		||||
	"Niemand hat die Absicht eine Mauer zu bauen",
 | 
			
		||||
	"Den Sozialismus in seinem Lauf, hält weder Ochs noch Esel auf.",
 | 
			
		||||
	"Frieden ist nicht alles, aber ohne Frieden ist alles nichts.",
 | 
			
		||||
	"Wir müssen lernen, wie die Gesellschaft so zu gestalten, dass die Menschen glücklich sind.",
 | 
			
		||||
	"Die Partei hat immer recht.",
 | 
			
		||||
	"Vorwärts immer, rückwärts nimmer!",
 | 
			
		||||
	"Den Sozialismus in seinem Lauf, hält weder Ochs noch Esel auf.",
 | 
			
		||||
	"Frieden ist nicht alles, aber ohne Frieden ist alles nichts.",
 | 
			
		||||
	"Wir müssen lernen, wie die Gesellschaft so zu gestalten, dass die Menschen glücklich sind.",
 | 
			
		||||
	"Die Partei hat immer recht.",
 | 
			
		||||
	"Vorwärts immer, rückwärts nimmer!",
 | 
			
		||||
	"Wer kämpft, kann verlieren. Wer nicht kämpft, hat schon verloren.",
 | 
			
		||||
	"Es muss demokratisiert werden im tiefsten Sinne des Wortes, die Wirtschaft, die Wissenschaft, das kulturelle Leben.",
 | 
			
		||||
	"Freundschaft, das ist das schönste auf der Welt.",
 | 
			
		||||
	"Niemand hat die Absicht, eine Mauer zu errichten.",
 | 
			
		||||
	"Die Wahrheit ist, dass wir noch nicht da sind, wo wir sein sollten.",
 | 
			
		||||
	"Die Zukunft gehört dem Sozialismus.",
 | 
			
		||||
	"Der Weg der sozialistischen Partei ist der Weg des Volkes.",
 | 
			
		||||
	"Wir haben das Glück, in einer Zeit zu leben, in der wir Zeugen eines großen historischen Wandels sind.",
 | 
			
		||||
	"Sozialismus ist das Gegenteil von Egoismus.",
 | 
			
		||||
	"Die Kunst ist eine Waffe der Revolution.",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ddrquote() string {
 | 
			
		||||
	return ddrquotes[rand.Intn(len(ddrquotes))]
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
module github.com/rileys-trash-can/newtecrs82obs
 | 
			
		||||
 | 
			
		||||
go 1.22.1
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/andreykaipov/goobs v1.2.3
 | 
			
		||||
	github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/buger/jsonparser v1.1.1 // indirect
 | 
			
		||||
	github.com/gorilla/websocket v1.5.1 // indirect
 | 
			
		||||
	github.com/hashicorp/logutils v1.0.0 // indirect
 | 
			
		||||
	github.com/mmcloughlin/profile v0.1.1 // indirect
 | 
			
		||||
	github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
 | 
			
		||||
	golang.org/x/net v0.17.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.18.0 // indirect
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
github.com/andreykaipov/goobs v1.2.3 h1:zqqr8mYwPNLHmoVMRPAJ7tcafO/CR7xua7rPoDMpDZ8=
 | 
			
		||||
github.com/andreykaipov/goobs v1.2.3/go.mod h1:D7+36C+8xY/aCHhwWZlyHbqApAfGPc3mFN3uhUjjnk0=
 | 
			
		||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
 | 
			
		||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
 | 
			
		||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
 | 
			
		||||
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
 | 
			
		||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
 | 
			
		||||
github.com/mmcloughlin/profile v0.1.1 h1:jhDmAqPyebOsVDOCICJoINoLb/AnLBaUw58nFzxWS2w=
 | 
			
		||||
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
 | 
			
		||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
 | 
			
		||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 | 
			
		||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 | 
			
		||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
 | 
			
		||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
 | 
			
		||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
 | 
			
		||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 | 
			
		||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
 | 
			
		||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,378 @@
 | 
			
		|||
package nt8
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/tarm/serial"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Connection struct {
 | 
			
		||||
	*serial.Port
 | 
			
		||||
 | 
			
		||||
	LightCmdCh chan<- []CmdLight
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Open(name string) (*Connection, error) {
 | 
			
		||||
	p, err := serial.OpenPort(&serial.Config{
 | 
			
		||||
		Name: name,
 | 
			
		||||
		Baud: 9600,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := &Connection{p, nil}
 | 
			
		||||
	go c.handleCmds()
 | 
			
		||||
 | 
			
		||||
	return c, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Event interface {
 | 
			
		||||
	event()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type EventButton struct {
 | 
			
		||||
	Type      EventButtonType
 | 
			
		||||
	Direction Direction
 | 
			
		||||
 | 
			
		||||
	Value uint8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Direction uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	Down Direction = iota
 | 
			
		||||
	Up
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (*EventButton) event() {}
 | 
			
		||||
 | 
			
		||||
type EventButtonType uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	ButtonProgram     EventButtonType = 0b0010
 | 
			
		||||
	ButtonPreview                     = 0b0001
 | 
			
		||||
	ButtonAutoTakeDSK                 = 0b0011
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type EventSlider struct {
 | 
			
		||||
	Type EventSliderType
 | 
			
		||||
 | 
			
		||||
	Value uint8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*EventSlider) event() {}
 | 
			
		||||
 | 
			
		||||
type EventSliderType uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	SliderTbar EventSliderType = iota
 | 
			
		||||
	RotA
 | 
			
		||||
	RotB
 | 
			
		||||
	RotC
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type CmdLight struct {
 | 
			
		||||
	Type  EventButtonType
 | 
			
		||||
	Value uint8
 | 
			
		||||
 | 
			
		||||
	State Light
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Light uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	LightOn Light = iota
 | 
			
		||||
	LightOff
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// first nibble of first byte is ignored
 | 
			
		||||
func (c *Connection) WriteCmd(cmd [2]byte) (err error) {
 | 
			
		||||
	h := strings.ToUpper(hex.EncodeToString(cmd[:]))
 | 
			
		||||
 | 
			
		||||
	log.Printf("sending: ~%s\r", h[1:])
 | 
			
		||||
	_, err = fmt.Fprintf(c.Port, "~%s\r", h[1:])
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Connection) handleCmds() {
 | 
			
		||||
	ch := make(chan []CmdLight)
 | 
			
		||||
	c.LightCmdCh = ch
 | 
			
		||||
 | 
			
		||||
	ledmap := make(map[EventButtonType]uint8)
 | 
			
		||||
	ledmap[ButtonProgram] = 0xFF
 | 
			
		||||
	ledmap[ButtonPreview] = 0xFF
 | 
			
		||||
	ledmap[ButtonAutoTakeDSK] = 0xFF
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		changed := make(map[EventButtonType]bool)
 | 
			
		||||
 | 
			
		||||
		cmds := <-ch
 | 
			
		||||
		//log.Printf("got cmds:  %d %#v", len(cmds), nil)
 | 
			
		||||
 | 
			
		||||
		for _, cmd := range cmds {
 | 
			
		||||
			var (
 | 
			
		||||
				oldstate = T(ledmap[cmd.Type]&(1<<cmd.Value) == 0, LightOn, LightOff)
 | 
			
		||||
				state    = cmd.State
 | 
			
		||||
			)
 | 
			
		||||
 | 
			
		||||
			// log.Printf("oldstate: %01b state %01b", oldstate, state)
 | 
			
		||||
 | 
			
		||||
			if oldstate != state {
 | 
			
		||||
				ledmap[cmd.Type] ^= (1 << cmd.Value)
 | 
			
		||||
				changed[cmd.Type] = true
 | 
			
		||||
				// send updated state:
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for t := range changed {
 | 
			
		||||
			err := c.WriteCmd([2]byte{
 | 
			
		||||
				byte(t),
 | 
			
		||||
				ledmap[t],
 | 
			
		||||
			})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Printf("Failed to wrtie CMD: %s", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// does fancy blinky blinky sequence
 | 
			
		||||
func (conn *Connection) InitBlynk() {
 | 
			
		||||
	cmd := CmdLight{}
 | 
			
		||||
	c := time.NewTicker(time.Second / 16)
 | 
			
		||||
 | 
			
		||||
	for i := uint8(0); i < 8; i++ {
 | 
			
		||||
		<-c.C
 | 
			
		||||
		cmd.Type = ButtonProgram
 | 
			
		||||
		cmd.Value = 7 - i
 | 
			
		||||
		cmd.State = LightOn
 | 
			
		||||
 | 
			
		||||
		conn.LightCmdCh <- []CmdLight{cmd}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint8(0); i < 8; i++ {
 | 
			
		||||
		<-c.C
 | 
			
		||||
		cmd.Type = ButtonPreview
 | 
			
		||||
		cmd.Value = 7 - i
 | 
			
		||||
		cmd.State = LightOn
 | 
			
		||||
 | 
			
		||||
		conn.LightCmdCh <- []CmdLight{cmd}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint8(0); i < 4; i++ {
 | 
			
		||||
		<-c.C
 | 
			
		||||
		cmd.Type = ButtonAutoTakeDSK
 | 
			
		||||
		cmd.Value = 7 - i
 | 
			
		||||
		cmd.State = LightOn
 | 
			
		||||
 | 
			
		||||
		conn.LightCmdCh <- []CmdLight{cmd}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint8(0); i < 3; i++ {
 | 
			
		||||
		<-c.C
 | 
			
		||||
		cmd.Type = ButtonAutoTakeDSK
 | 
			
		||||
		cmd.Value = i
 | 
			
		||||
		cmd.State = LightOn
 | 
			
		||||
 | 
			
		||||
		conn.LightCmdCh <- []CmdLight{cmd}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// off
 | 
			
		||||
	for i := uint8(0); i < 8; i++ {
 | 
			
		||||
		<-c.C
 | 
			
		||||
		cmd.Type = ButtonProgram
 | 
			
		||||
		cmd.Value = 7 - i
 | 
			
		||||
		cmd.State = LightOff
 | 
			
		||||
 | 
			
		||||
		conn.LightCmdCh <- []CmdLight{cmd}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint8(0); i < 8; i++ {
 | 
			
		||||
		<-c.C
 | 
			
		||||
		cmd.Type = ButtonPreview
 | 
			
		||||
		cmd.Value = 7 - i
 | 
			
		||||
		cmd.State = LightOff
 | 
			
		||||
 | 
			
		||||
		conn.LightCmdCh <- []CmdLight{cmd}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint8(0); i < 4; i++ {
 | 
			
		||||
		<-c.C
 | 
			
		||||
		cmd.Type = ButtonAutoTakeDSK
 | 
			
		||||
		cmd.Value = 7 - i
 | 
			
		||||
		cmd.State = LightOff
 | 
			
		||||
 | 
			
		||||
		conn.LightCmdCh <- []CmdLight{cmd}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint8(0); i < 3; i++ {
 | 
			
		||||
		<-c.C
 | 
			
		||||
		cmd.Type = ButtonAutoTakeDSK
 | 
			
		||||
		cmd.Value = i
 | 
			
		||||
		cmd.State = LightOff
 | 
			
		||||
 | 
			
		||||
		conn.LightCmdCh <- []CmdLight{cmd}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.Stop()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Connection) ReadCh() <-chan Event {
 | 
			
		||||
	ch := make(chan Event, 2)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		r := bufio.NewReader(c.Port)
 | 
			
		||||
		cmd := make([]byte, 2)
 | 
			
		||||
 | 
			
		||||
		var (
 | 
			
		||||
			program byte = 0xFF
 | 
			
		||||
			preview byte = 0xFF
 | 
			
		||||
 | 
			
		||||
			atdst byte = 0xFF
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
		for {
 | 
			
		||||
			hcmd, err := r.ReadBytes('\r')
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Printf("Error reading: %s", err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if errors.Is(err, io.EOF) {
 | 
			
		||||
				log.Printf("FATAL; EOF")
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if len(hcmd) != 5 {
 | 
			
		||||
				log.Printf("illigal command received: %02X %s %d", hcmd, hcmd, len(hcmd))
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			//log.Printf("%v %s", hcmd[1:4], hcmd[1:4])
 | 
			
		||||
 | 
			
		||||
			_, err = hex.Decode(cmd, append([]byte("0"), hcmd[1:4]...))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Printf("Failed to decode hex command: %s", err)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			//log.Printf("%08b %d", cmd, len(cmd))
 | 
			
		||||
 | 
			
		||||
			segment := cmd[0]
 | 
			
		||||
			//log.Printf("Segment: 0b %04b   0x %1X", segment, segment)
 | 
			
		||||
 | 
			
		||||
			switch segment {
 | 
			
		||||
			case 0b0010: // program
 | 
			
		||||
				for i := uint8(0); i < 8; i++ {
 | 
			
		||||
					mask := byte(1 << i)
 | 
			
		||||
 | 
			
		||||
					state := cmd[1] & mask
 | 
			
		||||
					oldstate := program & mask
 | 
			
		||||
 | 
			
		||||
					if state != oldstate {
 | 
			
		||||
						/*						log.Printf("mask: %08b", mask)
 | 
			
		||||
												log.Printf("sta:  %08b", state)
 | 
			
		||||
												log.Printf("osta: %08b", oldstate)
 | 
			
		||||
						*/
 | 
			
		||||
 | 
			
		||||
						program ^= mask // toggle oldstate
 | 
			
		||||
 | 
			
		||||
						ch <- &EventButton{
 | 
			
		||||
							Type:      ButtonProgram,
 | 
			
		||||
							Direction: T(state == 0, Down, Up),
 | 
			
		||||
 | 
			
		||||
							Value: i,
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			case 0b0001: // preview
 | 
			
		||||
				for i := uint8(0); i < 8; i++ {
 | 
			
		||||
					mask := byte(1 << i)
 | 
			
		||||
 | 
			
		||||
					state := cmd[1] & mask
 | 
			
		||||
					oldstate := preview & mask
 | 
			
		||||
 | 
			
		||||
					if state != oldstate {
 | 
			
		||||
						/*						log.Printf("mask: %08b", mask)
 | 
			
		||||
												log.Printf("sta:  %08b", state)
 | 
			
		||||
												log.Printf("osta: %08b", oldstate)
 | 
			
		||||
						*/
 | 
			
		||||
						preview ^= mask // toggle oldstate
 | 
			
		||||
 | 
			
		||||
						ch <- &EventButton{
 | 
			
		||||
							Type:      ButtonPreview,
 | 
			
		||||
							Direction: T(state == 0, Down, Up),
 | 
			
		||||
 | 
			
		||||
							Value: i,
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			case 0b0011: // atdsk
 | 
			
		||||
				for i := uint8(0); i < 8; i++ {
 | 
			
		||||
					mask := byte(1 << i)
 | 
			
		||||
 | 
			
		||||
					state := cmd[1] & mask
 | 
			
		||||
					oldstate := atdst & mask
 | 
			
		||||
 | 
			
		||||
					if state != oldstate {
 | 
			
		||||
						/*						log.Printf("mask: %08b", mask)
 | 
			
		||||
												log.Printf("sta:  %08b", state)
 | 
			
		||||
												log.Printf("osta: %08b", oldstate)
 | 
			
		||||
						*/
 | 
			
		||||
 | 
			
		||||
						atdst ^= mask // toggle oldstate
 | 
			
		||||
 | 
			
		||||
						ch <- &EventButton{
 | 
			
		||||
							Type:      ButtonAutoTakeDSK,
 | 
			
		||||
							Direction: T(state == 0, Down, Up),
 | 
			
		||||
 | 
			
		||||
							Value: i,
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			case 0b0100: // slider1
 | 
			
		||||
				ch <- &EventSlider{
 | 
			
		||||
					Type: SliderTbar,
 | 
			
		||||
 | 
			
		||||
					Value: cmd[1],
 | 
			
		||||
				}
 | 
			
		||||
			case 0b0101: // slider1
 | 
			
		||||
				ch <- &EventSlider{
 | 
			
		||||
					Type: RotA,
 | 
			
		||||
 | 
			
		||||
					Value: cmd[1],
 | 
			
		||||
				}
 | 
			
		||||
			case 0b0110: // slider1
 | 
			
		||||
				ch <- &EventSlider{
 | 
			
		||||
					Type: RotB,
 | 
			
		||||
 | 
			
		||||
					Value: cmd[1],
 | 
			
		||||
				}
 | 
			
		||||
			case 0b0111: // slider1
 | 
			
		||||
				ch <- &EventSlider{
 | 
			
		||||
					Type: RotC,
 | 
			
		||||
 | 
			
		||||
					Value: cmd[1],
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return ch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func T[K any](c bool, a, b K) K {
 | 
			
		||||
	if c {
 | 
			
		||||
		return a
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return b
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue