Compare commits
No commits in common. "main" and "v0.2" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,2 @@
|
|||||||
config.yaml
|
config.yaml
|
||||||
midi-request-trigger
|
midi-request-trigger
|
||||||
midi-request-trigger.log
|
|
16
Dockerfile
16
Dockerfile
@ -1,16 +0,0 @@
|
|||||||
FROM golang:1.20
|
|
||||||
|
|
||||||
# Build app
|
|
||||||
WORKDIR /app
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
RUN go mod download
|
|
||||||
COPY *.go ./
|
|
||||||
RUN go build -o /midi-request-trigger
|
|
||||||
WORKDIR /app
|
|
||||||
RUN rm -Rf /app; mkdir /etc/midi-request-trigger
|
|
||||||
|
|
||||||
# Configuration volume
|
|
||||||
VOLUME ["/etc/midi-request-trigger"]
|
|
||||||
|
|
||||||
# Command
|
|
||||||
CMD ["/midi-request-trigger"]
|
|
94
README.md
94
README.md
@ -70,11 +70,7 @@ On MacOS, you can setup a Launch Agent in `~/Library/LaunchAgents/com.mrgeckosme
|
|||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
```
|
|
||||||
|
|
||||||
For local network connection, you need to code sign your build.
|
|
||||||
```bash
|
|
||||||
codesign -s - --force --deep /path/to/bin/midi-request-trigger
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Start with:
|
Start with:
|
||||||
@ -114,7 +110,7 @@ On MacOS, there is an IAC Driver that can be enabled in Audio MIDI Setup.
|
|||||||
midi_routers:
|
midi_routers:
|
||||||
- name: service_notifications
|
- name: service_notifications
|
||||||
device: IAC Driver Bus 1
|
device: IAC Driver Bus 1
|
||||||
log_level: 2
|
debug_listener: true
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example note trigger configuration
|
### Example note trigger configuration
|
||||||
@ -124,7 +120,7 @@ midi_routers:
|
|||||||
midi_routers:
|
midi_routers:
|
||||||
- name: service_notifications
|
- name: service_notifications
|
||||||
device: IAC Driver Bus 1
|
device: IAC Driver Bus 1
|
||||||
log_level: 2
|
debug_listener: true
|
||||||
note_triggers:
|
note_triggers:
|
||||||
- channel: 0
|
- channel: 0
|
||||||
note: 0
|
note: 0
|
||||||
@ -140,7 +136,7 @@ midi_routers:
|
|||||||
midi_routers:
|
midi_routers:
|
||||||
- name: service_notifications
|
- name: service_notifications
|
||||||
device: IAC Driver Bus 1
|
device: IAC Driver Bus 1
|
||||||
log_level: 2
|
debug_listener: true
|
||||||
request_triggers:
|
request_triggers:
|
||||||
- channel: 0
|
- channel: 0
|
||||||
note: 0
|
note: 0
|
||||||
@ -156,7 +152,7 @@ midi_routers:
|
|||||||
midi_routers:
|
midi_routers:
|
||||||
- name: service_notifications
|
- name: service_notifications
|
||||||
device: IAC Driver Bus 1
|
device: IAC Driver Bus 1
|
||||||
log_level: 3
|
debug_listener: true
|
||||||
note_triggers:
|
note_triggers:
|
||||||
- channel: 0
|
- channel: 0
|
||||||
note: 0
|
note: 0
|
||||||
@ -178,85 +174,5 @@ midi_routers:
|
|||||||
headers:
|
headers:
|
||||||
Content-Type:
|
Content-Type:
|
||||||
- multipart/form-data; boundary=---------------------------888832887744
|
- multipart/form-data; boundary=---------------------------888832887744
|
||||||
```
|
debug_request: true
|
||||||
|
|
||||||
### Example mqtt config
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
---
|
|
||||||
midi_routers:
|
|
||||||
- name: Wing Midi Signals
|
|
||||||
device: WING Port 4
|
|
||||||
mqtt:
|
|
||||||
host: 10.0.0.2
|
|
||||||
port: 1883
|
|
||||||
client_id: midi_mqtt_bridge
|
|
||||||
user: mqtt
|
|
||||||
password: password
|
|
||||||
topic: midi/behringer_wing
|
|
||||||
log_level: 4
|
|
||||||
note_triggers:
|
|
||||||
- channel: 0
|
|
||||||
note: 1
|
|
||||||
match_all_velocities: true
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/enc/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "1"
|
|
||||||
- channel: 0
|
|
||||||
note: 2
|
|
||||||
match_all_velocities: true
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/enc/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "2"
|
|
||||||
- channel: 0
|
|
||||||
note: 3
|
|
||||||
match_all_velocities: true
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/enc/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "3"
|
|
||||||
- channel: 0
|
|
||||||
note: 4
|
|
||||||
match_all_velocities: true
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/enc/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "4"
|
|
||||||
- channel: 0
|
|
||||||
note: 5
|
|
||||||
match_all_velocities: true
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/enc/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "5"
|
|
||||||
- channel: 0
|
|
||||||
note: 6
|
|
||||||
match_all_velocities: true
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/enc/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "6"
|
|
||||||
- channel: 0
|
|
||||||
note: 7
|
|
||||||
match_all_velocities: true
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/enc/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "7"
|
|
||||||
- channel: 0
|
|
||||||
note: 8
|
|
||||||
match_all_velocities: true
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/enc/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "8"
|
|
||||||
- channel: 0
|
|
||||||
match_all_notes: true
|
|
||||||
match_all_velocities: true
|
|
||||||
delay_before: 200ms
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/bu/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "1"
|
|
||||||
- channel: 0
|
|
||||||
match_all_notes: true
|
|
||||||
match_all_velocities: true
|
|
||||||
delay_before: 200ms
|
|
||||||
mqtt_topic: osc/behringer_wing/send/$ctl/user/2/2/bu/val
|
|
||||||
mqtt_payload:
|
|
||||||
- "0"
|
|
||||||
delay_after: 200ms
|
|
||||||
```
|
```
|
||||||
|
131
config.go
131
config.go
@ -1,17 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"log"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/kkyr/fig"
|
"github.com/kkyr/fig"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configurations relating to HTTP server.
|
// Configurations relating to HTTP server.
|
||||||
@ -23,127 +19,9 @@ type HTTPConfig struct {
|
|||||||
Enabled bool `fig:"enabled"`
|
Enabled bool `fig:"enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuration for logging.
|
|
||||||
type LogConfig struct {
|
|
||||||
// Limit the log output by the log level.
|
|
||||||
Level string `fig:"level" yaml:"level" enum:"debug,info,warn,error" default:"info"`
|
|
||||||
// How should the log output be formatted.
|
|
||||||
Type string `fig:"type" yaml:"type" enum:"json,console" default:"console"`
|
|
||||||
// The outputs that the log should go to. Output of `console` will
|
|
||||||
// go to the stderr. An file path, will log to the file. Using `default-file`
|
|
||||||
// it'll either save to `/var/log/name.log`, or to the same directory as the
|
|
||||||
// executable if the path is not writable, or on Windows.
|
|
||||||
Outputs []string `fig:"outputs" yaml:"outputs" default:"console,default-file"`
|
|
||||||
// Maximum size of the log file in megabytes before it gets rotated.
|
|
||||||
MaxSize int `fig:"max_size" yaml:"max_size" default:"1"`
|
|
||||||
// Maximum number of backups to save.
|
|
||||||
MaxBackups int `fig:"max_backups" yaml:"max_backups" default:"3"`
|
|
||||||
// Maximum number of days to retain old log files.
|
|
||||||
MaxAge int `fig:"max_age" yaml:"max_age" default:"0"`
|
|
||||||
// Use the logal system time instead of UTC for file names of rotated backups.
|
|
||||||
LocalTime *bool `fig:"local_time" yaml:"local_time" default:"true"`
|
|
||||||
// Should the rotated logs be compressed.
|
|
||||||
Compress *bool `fig:"compress" yaml:"compress" default:"true"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply log config.
|
|
||||||
func (l *LogConfig) Apply() {
|
|
||||||
// Apply level.
|
|
||||||
switch l.Level {
|
|
||||||
case "debug":
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
case "info":
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
case "warn":
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
default:
|
|
||||||
log.SetLevel(log.ErrorLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply type.
|
|
||||||
switch l.Type {
|
|
||||||
case "json":
|
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
default:
|
|
||||||
log.SetFormatter(&log.TextFormatter{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change the outputs.
|
|
||||||
var outputs []io.Writer
|
|
||||||
for _, output := range l.Outputs {
|
|
||||||
// If output is console, add stderr and continue.
|
|
||||||
if output == "console" {
|
|
||||||
outputs = append(outputs, os.Stderr)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If default-file defined, find the default file.
|
|
||||||
if output == "default-file" {
|
|
||||||
var f *os.File
|
|
||||||
var err error
|
|
||||||
var logDir, logPath string
|
|
||||||
logName := fmt.Sprintf("%s.log", serviceName)
|
|
||||||
|
|
||||||
// On *nix, `/var/log/` should be default if writable.
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
logDir = "/var/log"
|
|
||||||
logPath = filepath.Join(logDir, logName)
|
|
||||||
|
|
||||||
// Verify we can write to log file.
|
|
||||||
f, err = os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we could not open the file, then we should try the executable path.
|
|
||||||
if err != nil || f == nil {
|
|
||||||
exe, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Unable to find an writable log path to save log to.")
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
logDir = filepath.Dir(exe)
|
|
||||||
logPath = filepath.Join(logDir, logName)
|
|
||||||
|
|
||||||
// Verify we can write to log file.
|
|
||||||
f, err = os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Unable to find an writable log path to save log to.")
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Close file.
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the config log path.
|
|
||||||
output = logPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup lumberjack log rotate for the output, and add to the list.
|
|
||||||
logFile := &lumberjack.Logger{
|
|
||||||
Filename: output,
|
|
||||||
MaxSize: l.MaxSize,
|
|
||||||
MaxBackups: l.MaxBackups,
|
|
||||||
MaxAge: l.MaxAge,
|
|
||||||
LocalTime: *l.LocalTime,
|
|
||||||
Compress: *l.Compress,
|
|
||||||
}
|
|
||||||
outputs = append(outputs, logFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are outputs, set the outputs.
|
|
||||||
if len(outputs) != 0 {
|
|
||||||
mw := io.MultiWriter(outputs...)
|
|
||||||
log.SetOutput(mw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configuration Structure.
|
// Configuration Structure.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
HTTP HTTPConfig `fig:"http"`
|
HTTP HTTPConfig `fig:"http"`
|
||||||
Log *LogConfig `fig:"log" yaml:"log"`
|
|
||||||
MidiRouters []*MidiRouter `fig:"midi_routers"`
|
MidiRouters []*MidiRouter `fig:"midi_routers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +48,7 @@ func (a *App) ReadConfig() {
|
|||||||
} else if _, err := os.Stat(etcConfig); err == nil {
|
} else if _, err := os.Stat(etcConfig); err == nil {
|
||||||
configFile = etcConfig
|
configFile = etcConfig
|
||||||
} else {
|
} else {
|
||||||
log.Println("Unable to find a configuration file.")
|
log.Fatal("Unable to find a configuration file.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the configuration file.
|
// Load the configuration file.
|
||||||
@ -181,7 +59,6 @@ func (a *App) ReadConfig() {
|
|||||||
Debug: true,
|
Debug: true,
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
},
|
},
|
||||||
Log: &LogConfig{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load configuration.
|
// Load configuration.
|
||||||
@ -191,7 +68,6 @@ func (a *App) ReadConfig() {
|
|||||||
fig.Dirs(filePath),
|
fig.Dirs(filePath),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.config = config
|
|
||||||
log.Printf("Error parsing configuration: %s\n", err)
|
log.Printf("Error parsing configuration: %s\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -204,9 +80,6 @@ func (a *App) ReadConfig() {
|
|||||||
config.HTTP.Port = app.flags.HTTPPort
|
config.HTTP.Port = app.flags.HTTPPort
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply log configs.
|
|
||||||
config.Log.Apply()
|
|
||||||
|
|
||||||
// Set global config structure.
|
// Set global config structure.
|
||||||
app.config = config
|
app.config = config
|
||||||
}
|
}
|
||||||
|
26
go.mod
26
go.mod
@ -1,27 +1,21 @@
|
|||||||
module github.com/GRMrGecko/midi-request-trigger
|
module github.com/GRMrGecko/midi-request-trigger
|
||||||
|
|
||||||
go 1.24.2
|
go 1.20
|
||||||
|
|
||||||
toolchain go1.24.4
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/eclipse/paho.mqtt.golang v1.5.0
|
github.com/eclipse/paho.mqtt.golang v1.5.0
|
||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.1
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/kkyr/fig v0.5.0
|
github.com/kkyr/fig v0.3.2
|
||||||
github.com/sirupsen/logrus v1.9.3
|
gitlab.com/gomidi/midi/v2 v2.0.30
|
||||||
gitlab.com/gomidi/midi/v2 v2.3.14
|
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.1 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
github.com/pelletier/go-toml v1.9.3 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
golang.org/x/net v0.27.0 // indirect
|
||||||
golang.org/x/net v0.41.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/sync v0.15.0 // indirect
|
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
38
go.sum
38
go.sum
@ -1,64 +1,26 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
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/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
|
github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
|
||||||
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
|
github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
|
||||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
|
||||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||||
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
|
||||||
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
|
||||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/kkyr/fig v0.3.2 h1:+vMj52FL6RJUxeKOBB6JXIMyyi1/2j1ERDrZXjoBjzM=
|
github.com/kkyr/fig v0.3.2 h1:+vMj52FL6RJUxeKOBB6JXIMyyi1/2j1ERDrZXjoBjzM=
|
||||||
github.com/kkyr/fig v0.3.2/go.mod h1:ItUILF8IIzgZOMhx5xpJ1W/bviQsWRKOwKXfE/tqUoA=
|
github.com/kkyr/fig v0.3.2/go.mod h1:ItUILF8IIzgZOMhx5xpJ1W/bviQsWRKOwKXfE/tqUoA=
|
||||||
github.com/kkyr/fig v0.5.0 h1:D4ym5MYYScOSgqyx1HYQaqFn9dXKzIuSz8N6SZ4rzqM=
|
|
||||||
github.com/kkyr/fig v0.5.0/go.mod h1:U4Rq/5eUNJ8o5UvOEc9DiXtNf41srOLn2r/BfCyuc58=
|
|
||||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
|
||||||
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
|
||||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
|
||||||
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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
gitlab.com/gomidi/midi/v2 v2.0.30 h1:RgRYbQeQSab5ZaP1lqRcCTnTSBQroE3CE6V9HgMmOAc=
|
gitlab.com/gomidi/midi/v2 v2.0.30 h1:RgRYbQeQSab5ZaP1lqRcCTnTSBQroE3CE6V9HgMmOAc=
|
||||||
gitlab.com/gomidi/midi/v2 v2.0.30/go.mod h1:Y6IFFyABN415AYsFMPJb0/43TRIuVYDpGKp2gDYLTLI=
|
gitlab.com/gomidi/midi/v2 v2.0.30/go.mod h1:Y6IFFyABN415AYsFMPJb0/43TRIuVYDpGKp2gDYLTLI=
|
||||||
gitlab.com/gomidi/midi/v2 v2.3.14 h1:BbTDExFlg0zm90AtyGDdO87jdKjn+eYqeSlSGGpFPzQ=
|
|
||||||
gitlab.com/gomidi/midi/v2 v2.3.14/go.mod h1:jDpP4O4skYi+7iVwt6Zyp18bd2M4hkjtMuw2cmgKgfw=
|
|
||||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
|
||||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
|
||||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
|
||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
2
http.go
2
http.go
@ -4,13 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Basic HTTP server structure.
|
// Basic HTTP server structure.
|
||||||
|
4
main.go
4
main.go
@ -3,11 +3,11 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"gitlab.com/gomidi/midi/v2"
|
"gitlab.com/gomidi/midi/v2"
|
||||||
_ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv"
|
_ "gitlab.com/gomidi/midi/v2/drivers/rtmididrv"
|
||||||
)
|
)
|
||||||
@ -15,7 +15,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
serviceName = "midi-request-trigger"
|
serviceName = "midi-request-trigger"
|
||||||
serviceDescription = "Takes trigger MIDI messages by HTTP or MQTT requests and trigger HTTP or MQTT requests by MIDI messages"
|
serviceDescription = "Takes trigger MIDI messages by HTTP or MQTT requests and trigger HTTP or MQTT requests by MIDI messages"
|
||||||
serviceVersion = "0.4.1"
|
serviceVersion = "0.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// App is the global application structure for communicating between servers and storing information.
|
// App is the global application structure for communicating between servers and storing information.
|
||||||
|
141
midiRouter.go
141
midiRouter.go
@ -5,15 +5,14 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"gitlab.com/gomidi/midi/v2"
|
"gitlab.com/gomidi/midi/v2"
|
||||||
"gitlab.com/gomidi/midi/v2/drivers"
|
"gitlab.com/gomidi/midi/v2/drivers"
|
||||||
)
|
)
|
||||||
@ -22,10 +21,8 @@ import (
|
|||||||
type LogLevel int
|
type LogLevel int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Logs info messages.
|
// Logs only errors.
|
||||||
InfoLog LogLevel = iota
|
ErrorLog LogLevel = iota
|
||||||
// Log only errors.
|
|
||||||
ErrorLog
|
|
||||||
// MQTT, HTTP, and MIDI receive logging.
|
// MQTT, HTTP, and MIDI receive logging.
|
||||||
ReceiveLog
|
ReceiveLog
|
||||||
// MQTT, HTTP, and MIDI send logging.
|
// MQTT, HTTP, and MIDI send logging.
|
||||||
@ -36,7 +33,7 @@ const (
|
|||||||
|
|
||||||
// Provides a string value for a log level.
|
// Provides a string value for a log level.
|
||||||
func (l LogLevel) String() string {
|
func (l LogLevel) String() string {
|
||||||
return [...]string{"Info", "Error", "Receive", "Send", "Debug"}[l]
|
return [...]string{"Error", "Receive", "Send", "Debug"}[l]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configurations relating to MQTT connection.
|
// Configurations relating to MQTT connection.
|
||||||
@ -73,25 +70,20 @@ type MQTTPayload struct {
|
|||||||
|
|
||||||
// Triggers that occur from MIDI messages received.
|
// Triggers that occur from MIDI messages received.
|
||||||
type NoteTrigger struct {
|
type NoteTrigger struct {
|
||||||
|
// If set, every note played will be matched.
|
||||||
|
MatchAllNotes bool `fig:"match_all_notes"`
|
||||||
// Channel to match.
|
// Channel to match.
|
||||||
Channel uint8 `fig:"channel"`
|
Channel uint8 `fig:"channel"`
|
||||||
// If we should match all channel values.
|
|
||||||
MatchAllChannels bool `fig:"match_all_channels"`
|
|
||||||
// Note to match.
|
// Note to match.
|
||||||
Note uint8 `fig:"note"`
|
Note uint8 `fig:"note"`
|
||||||
// If we should match all note values.
|
|
||||||
MatchAllNotes bool `fig:"match_all_notes"`
|
|
||||||
// Velocity to match.
|
// Velocity to match.
|
||||||
Velocity uint8 `fig:"velocity"`
|
Velocity uint8 `fig:"velocity"`
|
||||||
// If we should match all velocity values.
|
// If we should match all velocity values.
|
||||||
MatchAllVelocities bool `fig:"match_all_velocities"`
|
MatchAllVelocities bool `fig:"match_all_velocities"`
|
||||||
// Allow delaying the request.
|
|
||||||
DelayBefore time.Duration `fig:"delay_before"`
|
|
||||||
DelayAfter time.Duration `fig:"deplay_after"`
|
|
||||||
// Custom MQTT message. Do not set to ignore MQTT.
|
// Custom MQTT message. Do not set to ignore MQTT.
|
||||||
MqttTopic string `fig:"mqtt_topic"`
|
MqttTopic string `fig:"mqtt_topic"`
|
||||||
// Nil payload will generate a payload with midi info.
|
// Nil payload will generate a payload with midi info.
|
||||||
MqttPayload interface{} `fig:"mqtt_payload"`
|
MqttPayload []interface{} `fig:"mqtt_payload"`
|
||||||
// If the HTTP request should includ midi info.
|
// If the HTTP request should includ midi info.
|
||||||
MidiInfoInRequest bool `fig:"midi_info_in_request"`
|
MidiInfoInRequest bool `fig:"midi_info_in_request"`
|
||||||
// Should SSL requests require a valid certificate.
|
// Should SSL requests require a valid certificate.
|
||||||
@ -128,7 +120,7 @@ type RequestTrigger struct {
|
|||||||
type MidiRouter struct {
|
type MidiRouter struct {
|
||||||
// Used for human readable config.
|
// Used for human readable config.
|
||||||
Name string `fig:"name"`
|
Name string `fig:"name"`
|
||||||
// Midi device to connect, accepts regular expression.
|
// Midi device to connect.
|
||||||
Device string `fig:"device"`
|
Device string `fig:"device"`
|
||||||
// MQTT Connection if you are to integrate with MQTT.
|
// MQTT Connection if you are to integrate with MQTT.
|
||||||
MQTT MQTTConfig `fig:"mqtt"`
|
MQTT MQTTConfig `fig:"mqtt"`
|
||||||
@ -140,19 +132,18 @@ type MidiRouter struct {
|
|||||||
RequestTriggers []RequestTrigger `fig:"request_triggers"`
|
RequestTriggers []RequestTrigger `fig:"request_triggers"`
|
||||||
|
|
||||||
// How much logging.
|
// How much logging.
|
||||||
// 0 - Info
|
// 0 - Errors
|
||||||
// 1 - Errors
|
// 1 - MQTT and OSC receive logging.
|
||||||
// 2 - MQTT, HTTP, and MIDI receive logging.
|
// 2 - MQTT and OSC send logging.
|
||||||
// 3 - MQTT, HTTP, and MIDI send logging.
|
// 3 - Debug
|
||||||
// 4 - Debug
|
LogLevel LogLevel `yaml:"log_level" json:"log_level"`
|
||||||
LogLevel LogLevel `fig:"log_level"`
|
|
||||||
|
|
||||||
// Connection to MIDI device.
|
// Connection to MIDI device.
|
||||||
MidiOut drivers.Out `fig:"-"`
|
MidiOut drivers.Out `fig:"-"`
|
||||||
// Function to stop listening to MIDI device.
|
// Function to stop listening to MIDI device.
|
||||||
ListenerStop func() `fig:"-"`
|
ListenerStop func() `fig:"-"`
|
||||||
// The client connection to MQTT.
|
// The client connection to MQTT.
|
||||||
MqttClient mqtt.Client `fig:"-"`
|
MqttClient mqtt.Client `yaml:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging function to allow log levels.
|
// Logging function to allow log levels.
|
||||||
@ -165,7 +156,7 @@ func (r *MidiRouter) Log(level LogLevel, format string, args ...interface{}) {
|
|||||||
// When a MIDI message occurs, send the HTTP request.
|
// When a MIDI message occurs, send the HTTP request.
|
||||||
func (r *MidiRouter) sendRequest(channel, note, velocity uint8) {
|
func (r *MidiRouter) sendRequest(channel, note, velocity uint8) {
|
||||||
// If MQTT firehose not disabled, send to general cmd topic.
|
// If MQTT firehose not disabled, send to general cmd topic.
|
||||||
if r.MqttClient != nil && !r.MQTT.DisableMidiFirehose {
|
if r.MQTT.Host != "" && r.MQTT.Port != 0 && !r.MQTT.DisableMidiFirehose {
|
||||||
payload := MQTTPayload{
|
payload := MQTTPayload{
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
Note: note,
|
Note: note,
|
||||||
@ -177,7 +168,7 @@ func (r *MidiRouter) sendRequest(channel, note, velocity uint8) {
|
|||||||
} else {
|
} else {
|
||||||
topic := r.MQTT.Topic + "/cmd"
|
topic := r.MQTT.Topic + "/cmd"
|
||||||
r.MqttClient.Publish(topic, 0, true, data)
|
r.MqttClient.Publish(topic, 0, true, data)
|
||||||
r.Log(SendLog, "-> [MQTT] %s: %s", topic, string(data))
|
r.Log(SendLog, "-> [MQTT] %s", topic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,26 +177,15 @@ func (r *MidiRouter) sendRequest(channel, note, velocity uint8) {
|
|||||||
// If match all notes, process this request.
|
// If match all notes, process this request.
|
||||||
// If not, check if channel, note, and velocity matches.
|
// If not, check if channel, note, and velocity matches.
|
||||||
// The velocity may be defined to accept all.
|
// The velocity may be defined to accept all.
|
||||||
if (trig.Channel == channel || trig.MatchAllChannels) && (trig.Note == note || trig.MatchAllNotes) && (trig.Velocity == velocity || trig.MatchAllVelocities) {
|
if trig.MatchAllNotes || (trig.Channel == channel && trig.Note == note && (trig.Velocity == velocity || trig.MatchAllVelocities)) {
|
||||||
// For all logging, we want to print the message so setup a common string to print.
|
// For all logging, we want to print the message so setup a common string to print.
|
||||||
logInfo := fmt.Sprintf("note %s(%d) on channel %v with velocity %v", midi.Note(note), note, channel, velocity)
|
logInfo := fmt.Sprintf("note %s(%d) on channel %v with velocity %v", midi.Note(note), note, channel, velocity)
|
||||||
|
|
||||||
// Delay before.
|
if trig.MqttTopic != "" {
|
||||||
time.Sleep(trig.DelayBefore)
|
|
||||||
|
|
||||||
// If MQTT trigger, send the MQTT request.
|
|
||||||
if trig.MqttTopic != "" && r.MqttClient != nil {
|
|
||||||
// If payload provided, send the defined payload.
|
|
||||||
if trig.MqttPayload != nil {
|
if trig.MqttPayload != nil {
|
||||||
data, err := json.Marshal(trig.MqttPayload)
|
r.MqttClient.Publish(trig.MqttTopic, 0, true, trig.MqttPayload)
|
||||||
if err != nil {
|
r.Log(SendLog, "-> [MQTT] %s", trig.MqttTopic)
|
||||||
r.Log(ErrorLog, "Json Encode: %s", err)
|
|
||||||
} else {
|
} else {
|
||||||
r.MqttClient.Publish(trig.MqttTopic, 0, true, data)
|
|
||||||
r.Log(SendLog, "-> [MQTT] %s: %s", trig.MqttTopic, string(data))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If no payload provided, send the note information as JSON.
|
|
||||||
payload := MQTTPayload{
|
payload := MQTTPayload{
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
Note: note,
|
Note: note,
|
||||||
@ -216,12 +196,11 @@ func (r *MidiRouter) sendRequest(channel, note, velocity uint8) {
|
|||||||
r.Log(ErrorLog, "Json Encode: %s", err)
|
r.Log(ErrorLog, "Json Encode: %s", err)
|
||||||
} else {
|
} else {
|
||||||
r.MqttClient.Publish(trig.MqttTopic, 0, true, data)
|
r.MqttClient.Publish(trig.MqttTopic, 0, true, data)
|
||||||
r.Log(SendLog, "-> [MQTT] %s: %s", trig.MqttTopic, string(data))
|
r.Log(SendLog, "-> [MQTT] %s", trig.MqttTopic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If URL trigger defined, perform a HTTP request.
|
|
||||||
if trig.URL != "" {
|
if trig.URL != "" {
|
||||||
// Default method to GET if nothing is defined.
|
// Default method to GET if nothing is defined.
|
||||||
if trig.Method == "" {
|
if trig.Method == "" {
|
||||||
@ -290,9 +269,6 @@ func (r *MidiRouter) sendRequest(channel, note, velocity uint8) {
|
|||||||
r.Log(DebugLog, "Trigger response: %s\n%s", logInfo, string(body))
|
r.Log(DebugLog, "Trigger response: %s\n%s", logInfo, string(body))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delay after.
|
|
||||||
time.Sleep(trig.DelayAfter)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -479,60 +455,24 @@ func (r *MidiRouter) MqttSubscribe(topic string) {
|
|||||||
func (r *MidiRouter) Connect() {
|
func (r *MidiRouter) Connect() {
|
||||||
// If request triggers defined, find the out port.
|
// If request triggers defined, find the out port.
|
||||||
if len(r.RequestTriggers) != 0 {
|
if len(r.RequestTriggers) != 0 {
|
||||||
go func() {
|
out, err := midi.FindOutPort(r.Device)
|
||||||
deviceRx, err := regexp.Compile(r.Device)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to compile regexp of '%s': %v", r.Device, err)
|
log.Println("Can't find output device:", r.Device)
|
||||||
}
|
|
||||||
for {
|
|
||||||
var out drivers.Out
|
|
||||||
for _, device := range midi.GetOutPorts() {
|
|
||||||
if deviceRx.MatchString(device.String()) {
|
|
||||||
err = device.Open()
|
|
||||||
out = device
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if out == nil {
|
|
||||||
err = fmt.Errorf("unable to find matching device")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
r.Log(ErrorLog, "Failed to find output device '%s': %v", r.Device, err)
|
|
||||||
} else {
|
} else {
|
||||||
r.MidiOut = out
|
r.MidiOut = out
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Log(ErrorLog, "Retrying in 1 minute.")
|
|
||||||
time.Sleep(time.Minute)
|
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If listener is disabled, stop here.
|
// If listener is disabled, stop here.
|
||||||
if !r.DisableListener {
|
if r.DisableListener {
|
||||||
go func() {
|
return
|
||||||
deviceRx, err := regexp.Compile(r.Device)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to compile regexp of '%s': %v", r.Device, err)
|
|
||||||
}
|
}
|
||||||
for {
|
|
||||||
// Try finding input port.
|
// Try finding input port.
|
||||||
r.Log(InfoLog, "Connecting to input device: %s", r.Device)
|
log.Println("Connecting to device:", r.Device)
|
||||||
var in drivers.In
|
in, err := midi.FindInPort(r.Device)
|
||||||
for _, device := range midi.GetInPorts() {
|
|
||||||
if deviceRx.MatchString(device.String()) {
|
|
||||||
err = device.Open()
|
|
||||||
in = device
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if in == nil {
|
|
||||||
err = fmt.Errorf("unable to find matching device")
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Log(ErrorLog, "Can't find input device '%s': %v", r.Device, err)
|
log.Println("Can't find device:", r.Device)
|
||||||
r.Log(ErrorLog, "Retrying in 1 minute.")
|
return
|
||||||
time.Sleep(time.Minute)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start listening to MIDI messages.
|
// Start listening to MIDI messages.
|
||||||
@ -556,22 +496,13 @@ func (r *MidiRouter) Connect() {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Log(ErrorLog, "Error listening to device: %s", err)
|
r.Log(ErrorLog, "Error listening to device: %s", err)
|
||||||
r.Log(ErrorLog, "Retrying in 1 minute.")
|
return
|
||||||
time.Sleep(time.Minute)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
r.Log(InfoLog, "Connected to input device: %s", r.Device)
|
|
||||||
|
|
||||||
// Update stop function for disconnects.
|
// Update stop function for disconnects.
|
||||||
r.ListenerStop = stop
|
r.ListenerStop = stop
|
||||||
break
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.MQTT.Host != "" && r.MQTT.Port != 0 {
|
if r.MQTT.Host != "" && r.MQTT.Port != 0 {
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
// Connect to MQTT.
|
// Connect to MQTT.
|
||||||
mqtt_opts := mqtt.NewClientOptions()
|
mqtt_opts := mqtt.NewClientOptions()
|
||||||
mqtt_opts.AddBroker(fmt.Sprintf("tcp://%s:%d", r.MQTT.Host, r.MQTT.Port))
|
mqtt_opts.AddBroker(fmt.Sprintf("tcp://%s:%d", r.MQTT.Host, r.MQTT.Port))
|
||||||
@ -584,9 +515,7 @@ func (r *MidiRouter) Connect() {
|
|||||||
r.Log(DebugLog, "Connecting to MQTT")
|
r.Log(DebugLog, "Connecting to MQTT")
|
||||||
if t := r.MqttClient.Connect(); t.Wait() && t.Error() != nil {
|
if t := r.MqttClient.Connect(); t.Wait() && t.Error() != nil {
|
||||||
log.Fatalf("MQTT error: %s", t.Error())
|
log.Fatalf("MQTT error: %s", t.Error())
|
||||||
r.Log(ErrorLog, "Retrying in 1 minute.")
|
return
|
||||||
time.Sleep(time.Minute)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to MQTT topics.
|
// Subscribe to MQTT topics.
|
||||||
@ -601,9 +530,6 @@ func (r *MidiRouter) Connect() {
|
|||||||
r.MqttSubscribe(r.MQTT.Topic + "/" + trig.MqttSubTopic)
|
r.MqttSubscribe(r.MQTT.Topic + "/" + trig.MqttSubTopic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,7 +539,4 @@ func (r *MidiRouter) Disconnect() {
|
|||||||
if r.ListenerStop != nil {
|
if r.ListenerStop != nil {
|
||||||
r.ListenerStop()
|
r.ListenerStop()
|
||||||
}
|
}
|
||||||
if r.MqttClient != nil {
|
|
||||||
r.MqttClient.Disconnect(0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user