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