Initial commit

This commit is contained in:
Daniel Prows 2023-08-24 21:52:28 -06:00
parent 104a042e5e
commit ff229676d7
19 changed files with 227 additions and 159 deletions

View File

@ -1,6 +1,2 @@
# Stream Deck Go Plugin # Mac HTTP Proxy Control
find everywhere with {{{NAME}}} and replace it with appropriate values
do the manifest
write code

View File

@ -1,7 +1,7 @@
INSTALLDIR = ~/Library/Application\ Support/com.elgato.StreamDeck/Plugins/com.dlprows.{{{NAME}}}.sdPlugin INSTALLDIR = ~/Library/Application\ Support/com.elgato.StreamDeck/Plugins/com.dlprows.httpproxy.sdPlugin
#BUILDDIR = build #BUILDDIR = build
#RELEASEDIR = release #RELEASEDIR = release
#SDPLUGINDIR = "./com.dlprows.{{{NAME}}}.sdPlugin" #SDPLUGINDIR = "./com.dlprows.httpproxycontrol.sdPlugin"
update: update:
killall Stream\ Deck || true killall Stream\ Deck || true
@ -9,9 +9,9 @@ update:
open -a Elgato\ Stream\ Deck open -a Elgato\ Stream\ Deck
build: build:
go build -o com.dlprows.{{{NAME}}}.sdPlugin go build -o com.dlprows.httpproxy.sdPlugin
rm com.dlprows.{{{NAME}}}.streamDeckPlugin rm com.dlprows.httpproxy.streamDeckPlugin || true
./DistributionTool -b -i com.dlprows.{{{NAME}}}.sdPlugin -o . ./DistributionTool -b -i com.dlprows.httpproxy.sdPlugin -o .
#.PHONY: test install build logs #.PHONY: test install build logs

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,53 @@
{
"Actions": [
{
"UUID": "com.dlprows.httpproxy.action",
"Name": "System Proxy",
"Tooltip": "Turn on/off system proxy",
"Icon": "Images/icon",
"States": [
{
"Image": "Images/pluginAction",
"TitleAlignment": "middle",
"FontSize": "12"
},
{
"Image": "Images/pluginAction",
"TitleAlignment": "middle",
"FontSize": "12"
},
{
"Image": "Images/pluginAction",
"TitleAlignment": "middle",
"FontSize": "12"
},
{
"Image": "Images/pluginAction",
"TitleAlignment": "middle",
"FontSize": "12"
}
],
"Controllers": [ "Keypad" ],
"SupportedInMultiActions": false
}
],
"Author": "dlprows",
"Name": "Http Proxy Control",
"Description": "Turn on and off HTTP(S) proxy settings in mac os",
"URL": "https://encyclopediaofdaniel.com",
"Version": "1.0",
"CodePath": "httpproxycontrol",
"Category": "Networking [dlprows]",
"Icon": "Images/pluginIcon",
"CategoryIcon": "Images/categoryIcon",
"OS": [
{
"Platform": "mac",
"MinimumVersion": "13.0"
}
],
"SDKVersion": 2,
"Software": {
"MinimumVersion": "6.0"
}
}

View File

@ -1,49 +0,0 @@
{
"Actions": [
{
"UUID": "com.dlprows.{{{NAME}}}.dialaction",
"Name": "Volume",
"Tooltip": "Control system volume",
"Icon": "Images/icon",
"States": [
{
"Image": "Images/pluginAction",
"TitleAlignment": "middle",
"FontSize": "12"
}
],
"Controllers": [ "Encoder" ],
"Encoder": {
"background": "backgroundImage",
"Icon": "Images/actionIcon",
"layout": "$B1",
"StackColor": "#AABBCC",
"TriggerDescription": {
"Rotate": "System Volume",
"Push": "Mute",
"Touch": "Mute"
}
},
"SupportedInMultiActions": false
}
],
"Author": "dlprows",
"Name": "{{{NAME}}}",
"Description": "Control volume on mac",
"URL": "https://encyclopediaofdaniel.com",
"Version": "1.0",
"CodePath": "{{{NAME}}}",
"Category": "Volume Control [dlprows]",
"Icon": "Images/pluginIcon",
"CategoryIcon": "Images/categoryIcon",
"OS": [
{
"Platform": "mac",
"MinimumVersion": "13.0"
}
],
"SDKVersion": 2,
"Software": {
"MinimumVersion": "6.0"
}
}

View File

@ -1,7 +1,9 @@
module macvolumecontrol module httpproxycontrol
go 1.20 go 1.20
require code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk v1.0.1 require (
code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk v1.0.1 // indirect
require github.com/gorilla/websocket v1.5.0 // indirect github.com/b4b4r07/go-pipe v0.0.0-20191010045404-84b446f57366 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
)

View File

@ -1,6 +1,6 @@
code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk v1.0.1 h1:2IoDJrWt6Bp5IcXTtqjdygDo3Ruh5VHsS9R5MjI2WWQ= code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk v1.0.1 h1:2IoDJrWt6Bp5IcXTtqjdygDo3Ruh5VHsS9R5MjI2WWQ=
code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk v1.0.1/go.mod h1:SaMt3PIkPtNwkgrcLpD89GK/7tLaUkcEvOZaHUC66+4= code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk v1.0.1/go.mod h1:SaMt3PIkPtNwkgrcLpD89GK/7tLaUkcEvOZaHUC66+4=
github.com/b4b4r07/go-pipe v0.0.0-20191010045404-84b446f57366 h1:FVAkDbBxovi2ID0vaxY7vCGvdKASt9r0TGPM1u4YQyg=
github.com/b4b4r07/go-pipe v0.0.0-20191010045404-84b446f57366/go.mod h1:1ymsiQNa3qebVEEVtuIdhtAXRfjO4qFCFq1bBUOT2HE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/samwho/streamdeck v0.0.0-20190725183037-2b866fdcb4a6 h1:ocHB+wIjLCdideequ8l8dUem6DxHf/A4Mmo0TRxBf1U=
github.com/samwho/streamdeck v0.0.0-20190725183037-2b866fdcb4a6/go.mod h1:OjeKL1Q8xRGm1zHGmtu9JeQUFNOXXXIcSZvCNzRP3GM=

View File

@ -2,21 +2,17 @@ package main
import ( import (
"context" "context"
"encoding/json"
"fmt"
"log" "log"
"os" "os"
"time" "time"
"macvolumecontrol/logging" "httpproxycontrol/logging"
"macvolumecontrol/volume" "httpproxycontrol/networking"
"code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk" "code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk"
sdcontext "code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk/context" sdcontext "code.encyclopediaofdaniel.com/dlprows/streamdeck-sdk/context"
) )
var _currentSettings *volume.VolumeSettings = &volume.VolumeSettings{}
func main() { func main() {
logging.Enable() logging.Enable()
log.Println("Starting plugin") log.Println("Starting plugin")
@ -38,64 +34,26 @@ func main() {
func setup(client *streamdeck.Client) { func setup(client *streamdeck.Client) {
log.Println("Registering actions") log.Println("Registering actions")
action := client.Action("com.dlprows.macvolumecontrol.dialaction") action := client.Action("com.dlprows.httpproxy.action")
contexts := make(map[string]struct{}) contexts := make(map[string]struct{})
action.RegisterHandler(streamdeck.DialRotate, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error { action.RegisterHandler(streamdeck.KeyDown, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
log.Println("dial rotate") log.Println("Key Down")
p := streamdeck.DialRotatePayload[any]{} toggleVpn()
if err := json.Unmarshal(event.Payload, &p); err != nil { client.SetState(ctx, getState())
return err
}
//volume.ChangeVolumeWithKeyboard(p.Ticks) return nil
newSettings, err := volume.ChangeVolume(p.Ticks)
if err != nil {
return err
}
return setFeedbackIfNeeded(ctx, client, newSettings)
})
action.RegisterHandler(streamdeck.DialDown, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
log.Println("dial down")
newSettings, err := volume.ToggleMute()
if err != nil {
return err
}
return setFeedbackIfNeeded(ctx, client, newSettings)
})
action.RegisterHandler(streamdeck.TouchTap, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
log.Println("touch tap")
newSettings, err := volume.ToggleMute()
if err != nil {
return err
}
return setFeedbackIfNeeded(ctx, client, newSettings)
}) })
action.RegisterHandler(streamdeck.WillAppear, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error { action.RegisterHandler(streamdeck.WillAppear, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
log.Println("Will Appear") log.Println("Will Appear")
contexts[event.Context] = struct{}{} contexts[event.Context] = struct{}{}
newSettings, err := volume.GetVolumeSettings() client.SetState(ctx, getState())
return nil
if err != nil {
return err
}
return setFeedback(ctx, client, newSettings)
}) })
action.RegisterHandler(streamdeck.WillDisappear, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error { action.RegisterHandler(streamdeck.WillDisappear, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
@ -106,12 +64,7 @@ func setup(client *streamdeck.Client) {
//start background thread to keep the display up to date if changed outside the stream deck //start background thread to keep the display up to date if changed outside the stream deck
go func() { go func() {
for range time.Tick(time.Second * 1) { for range time.Tick(time.Second * 10) {
newSettings, err := volume.GetVolumeSettings()
if err != nil {
log.Fatal(err)
}
for ctxStr := range contexts { for ctxStr := range contexts {
//for each context //for each context
@ -119,55 +72,60 @@ func setup(client *streamdeck.Client) {
ctx := context.Background() ctx := context.Background()
ctx = sdcontext.WithContext(ctx, ctxStr) ctx = sdcontext.WithContext(ctx, ctxStr)
setFeedback(ctx, client, newSettings) client.SetState(ctx, getState())
} }
_currentSettings = newSettings
} }
}() }()
} }
func setFeedback(ctx context.Context, client *streamdeck.Client, newSettings *volume.VolumeSettings) error { func toggleVpn() error {
/*
if on, turn off
if off, turn on
if broken, turn off
*/
activeDevice, err := networking.GetActiveDevice()
payload := FeedbackPayload{} if err != nil {
return err
opacity := 1.0
if newSettings.OutputMuted {
opacity = 0.5
} }
payload.Value = ValueWithOpacity[string]{ enabled, err := networking.IsProxyEnabled(activeDevice)
fmt.Sprintf("%d%%", newSettings.OutputVolume),
opacity, if err != nil {
return err
} }
payload.Indicator = ValueWithOpacity[int]{ networking.SetProxyServer(activeDevice, "192.168.41.101", "8888")
newSettings.OutputVolume, networking.SetProxyState(activeDevice, !enabled)
opacity,
}
payload.Icon = ValueWithOpacity[any]{nil, opacity}
return client.SetFeedback(ctx, payload)
}
func setFeedbackIfNeeded(ctx context.Context, client *streamdeck.Client, newSettings *volume.VolumeSettings) error {
if _currentSettings.OutputVolume == newSettings.OutputVolume && _currentSettings.OutputMuted == newSettings.OutputMuted {
return nil return nil
}
func getState() int {
/*
0 for no network
1 for proxy off
2 for proxy on
3 for busted
*/
activeDevice, err := networking.GetActiveDevice()
if err != nil {
return 3
} }
_currentSettings = newSettings if activeDevice == "" {
return setFeedback(ctx, client, newSettings) return 0
} }
type FeedbackPayload struct { proxyEnabled, err := networking.IsProxyEnabled(activeDevice)
Value ValueWithOpacity[string] `json:"value"`
Indicator ValueWithOpacity[int] `json:"indicator"`
Icon ValueWithOpacity[any] `json:"icon"`
}
type ValueWithOpacity[T any] struct { if err != nil {
Value T `json:"value,omitempty"` return 3
Opacity float64 `json:"opacity"` }
if proxyEnabled {
return 2
}
return 1
} }

View File

@ -0,0 +1,108 @@
package networking
import (
"os/exec"
"strings"
)
func GetActiveDevice() (string, error) {
output, err := exec.Command("networksetup", "-listnetworkserviceorder").Output()
if err != nil {
return "", err
}
for _, line := range strings.Split(strings.TrimSuffix(string(output), "\n"), "\n") {
if !strings.Contains(line, "Device:") {
continue
}
//(Hardware Port: Thunderbolt Ethernet Slot 2, Device: en8)
split := strings.Split(line, ",")
name := strings.Split(split[0], ":")[1]
name = strings.TrimPrefix(name, " ")
device := strings.Split(split[1], ":")[1]
device = device[1 : len(device)-1]
active := isDeviceActive(device)
if active {
return name, nil
}
}
return "", nil
}
func isDeviceActive(device string) bool {
output, _ := exec.Command("ifconfig", device).Output()
for _, line := range strings.Split(strings.TrimSuffix(string(output), "\n"), "\n") {
if strings.Contains(line, "status: active") {
return true
}
}
return false
}
func IsProxyEnabled(deviceName string) (bool, error) {
output, err := exec.Command("networksetup", "-getwebproxy", deviceName).Output()
if err != nil {
return false, err
}
webEnabled := strings.Contains(string(output), "Enabled: Yes\n")
output, err = exec.Command("networksetup", "-getsecurewebproxy", deviceName).Output()
if err != nil {
return false, err
}
secureEnabled := strings.Contains(string(output), "Enabled: Yes\n")
//todo: if they're not equal, its some sort of error case
return webEnabled && secureEnabled, nil
}
func SetProxyState(deviceName string, on bool) error {
onOff := "on"
if !on {
onOff = "off"
}
err := exec.Command("networksetup", "-setwebproxystate", deviceName, onOff).Run()
if err != nil {
return err
}
err = exec.Command("networksetup", "-setsecurewebproxystate", deviceName, onOff).Run()
if err != nil {
return err
}
return nil
}
func SetProxyServer(deviceName string, host string, port string) error {
err := exec.Command("networksetup", "-setwebproxy", deviceName, host, port).Run()
if err != nil {
return err
}
err = exec.Command("networksetup", "-setsecurewebproxy", deviceName, host, port).Run()
if err != nil {
return err
}
return nil
}