Major restructure and added a few example plugins.
This commit is contained in:
parent
cad454a4bf
commit
8ee19c457a
10
action.go
Normal file
10
action.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package streamdeck
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
uuid string
|
||||||
|
handlers map[string][]EventHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (action *Action) RegisterHandler(eventName string, handler EventHandler) {
|
||||||
|
action.handlers[eventName] = append(action.handlers[eventName], handler)
|
||||||
|
}
|
Binary file not shown.
Before Width: | Height: | Size: 4.3 KiB |
BIN
actionIcon.png
BIN
actionIcon.png
Binary file not shown.
Before Width: | Height: | Size: 960 B |
205
client.go
Normal file
205
client.go
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
package streamdeck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
sdcontext "github.com/samwho/streamdeck/context"
|
||||||
|
"github.com/samwho/streamdeck/payload"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EventHandler func(ctx context.Context, client *Client, event Event) error
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
ctx context.Context
|
||||||
|
params RegistrationParams
|
||||||
|
c *websocket.Conn
|
||||||
|
actions map[string]*Action
|
||||||
|
handlers map[string][]EventHandler
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(ctx context.Context, params RegistrationParams) *Client {
|
||||||
|
return &Client{
|
||||||
|
ctx: ctx,
|
||||||
|
params: params,
|
||||||
|
actions: make(map[string]*Action),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (client *Client) Action(uuid string) *Action {
|
||||||
|
_, ok := client.actions[uuid]
|
||||||
|
if !ok {
|
||||||
|
client.actions[uuid] = &Action{
|
||||||
|
uuid: uuid,
|
||||||
|
handlers: make(map[string][]EventHandler),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return client.actions[uuid]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) Run() error {
|
||||||
|
interrupt := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(interrupt, os.Interrupt)
|
||||||
|
|
||||||
|
u := url.URL{Scheme: "ws", Host: fmt.Sprintf("127.0.0.1:%d", client.params.Port)}
|
||||||
|
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client.c = c
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(client.done)
|
||||||
|
for {
|
||||||
|
messageType, message, err := client.c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("read error: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if messageType == websocket.PingMessage {
|
||||||
|
log.Printf("received ping message\n")
|
||||||
|
if err := client.c.WriteMessage(websocket.PongMessage, []byte{}); err != nil {
|
||||||
|
log.Printf("error while ponging: %v\n", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
event := Event{}
|
||||||
|
if err := json.Unmarshal(message, &event); err != nil {
|
||||||
|
log.Printf("failed to unmarshal received event: %s\n", string(message))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("recv: ", string(message))
|
||||||
|
|
||||||
|
ctx := sdcontext.WithContext(client.ctx, event.Context)
|
||||||
|
ctx = sdcontext.WithDevice(ctx, event.Device)
|
||||||
|
ctx = sdcontext.WithAction(ctx, event.Action)
|
||||||
|
|
||||||
|
if event.Action == "" {
|
||||||
|
for _, f := range client.handlers[event.Event] {
|
||||||
|
if err := f(ctx, client, event); err != nil {
|
||||||
|
log.Printf("error in handler for event %v: %v\n", event.Event, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
action, ok := client.actions[event.Action]
|
||||||
|
if !ok {
|
||||||
|
log.Printf("received event for nonexistent action: %v\n", event.Action)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range action.handlers[event.Event] {
|
||||||
|
if err := f(ctx, client, event); err != nil {
|
||||||
|
log.Printf("error in handler for event %v: %v\n", event.Event, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := client.register(client.params); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-client.done:
|
||||||
|
return nil
|
||||||
|
case <-interrupt:
|
||||||
|
log.Printf("interrupted, closing...\n")
|
||||||
|
return client.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) register(params RegistrationParams) error {
|
||||||
|
if err := client.send(Event{UUID: params.PluginUUID, Event: params.RegisterEvent}); err != nil {
|
||||||
|
client.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) send(event Event) error {
|
||||||
|
j, _ := json.Marshal(event)
|
||||||
|
log.Printf("sending message: %v\n", string(j))
|
||||||
|
return client.c.WriteJSON(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) SetSettings(ctx context.Context, settings interface{}) error {
|
||||||
|
return client.send(NewEvent(ctx, SetSettings, settings))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) GetSettings(ctx context.Context) error {
|
||||||
|
return client.send(NewEvent(ctx, GetSettings, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) SetGlobalSettings(ctx context.Context, settings interface{}) error {
|
||||||
|
return client.send(NewEvent(ctx, SetGlobalSettings, settings))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) GetGlobalSettings(ctx context.Context) error {
|
||||||
|
return client.send(NewEvent(ctx, GetGlobalSettings, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) OpenURL(ctx context.Context, u url.URL) error {
|
||||||
|
return client.send(NewEvent(ctx, OpenURL, payload.OpenURL{URL: u.String()}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) LogMessage(message string) error {
|
||||||
|
return client.send(NewEvent(nil, LogMessage, payload.LogMessage{Message: message}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) SetTitle(ctx context.Context, title string, target payload.Target) error {
|
||||||
|
return client.send(NewEvent(ctx, SetTitle, payload.SetTitle{Title: title, Target: target}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) SetImage(ctx context.Context, base64image string, target payload.Target) error {
|
||||||
|
return client.send(NewEvent(ctx, SetImage, payload.SetImage{Base64Image: base64image, Target: target}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) ShowAlert(ctx context.Context) error {
|
||||||
|
return client.send(NewEvent(ctx, ShowAlert, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) ShowOk(ctx context.Context) error {
|
||||||
|
return client.send(NewEvent(ctx, ShowOk, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) SetState(ctx context.Context, state int) error {
|
||||||
|
return client.send(NewEvent(ctx, SetState, payload.SetState{State: state}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) SwitchToProfile(ctx context.Context, profile string) error {
|
||||||
|
return client.send(NewEvent(ctx, SwitchToProfile, payload.SwitchProfile{Profile: profile}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) SendToPropertyInspector(ctx context.Context, payload interface{}) error {
|
||||||
|
return client.send(NewEvent(ctx, SendToPropertyInspector, payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) SendToPlugin(ctx context.Context, payload interface{}) error {
|
||||||
|
return client.send(NewEvent(ctx, SendToPlugin, payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) Close() error {
|
||||||
|
err := client.c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-client.done:
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
}
|
||||||
|
return client.c.Close()
|
||||||
|
}
|
@ -1,7 +1,5 @@
|
|||||||
package streamdeck
|
package streamdeck
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DidReceiveSettings = "didReceiveSettings"
|
DidReceiveSettings = "didReceiveSettings"
|
||||||
DidReceiveGlobalSettings = "didReceiveGlobalSettings"
|
DidReceiveGlobalSettings = "didReceiveGlobalSettings"
|
||||||
@ -33,21 +31,3 @@ const (
|
|||||||
SetState = "setState"
|
SetState = "setState"
|
||||||
SwitchToProfile = "switchToProfile"
|
SwitchToProfile = "switchToProfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
type contextKeyType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
contextKey contextKeyType = iota
|
|
||||||
)
|
|
||||||
|
|
||||||
func getContext(ctx context.Context) string {
|
|
||||||
if ctx == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx.Value(contextKey).(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setContext(ctx context.Context, streamdeckContext string) context.Context {
|
|
||||||
return context.WithValue(ctx, contextKey, streamdeckContext)
|
|
||||||
}
|
|
55
context/context.go
Normal file
55
context/context.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type keyType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
contextKey keyType = iota
|
||||||
|
deviceKey
|
||||||
|
actionKey
|
||||||
|
)
|
||||||
|
|
||||||
|
func Context(ctx context.Context) string {
|
||||||
|
return get(ctx, contextKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithContext(ctx context.Context, streamdeckContext string) context.Context {
|
||||||
|
return context.WithValue(ctx, contextKey, streamdeckContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Device(ctx context.Context) string {
|
||||||
|
return get(ctx, deviceKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDevice(ctx context.Context, streamdeckDevice string) context.Context {
|
||||||
|
return context.WithValue(ctx, deviceKey, streamdeckDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Action(ctx context.Context) string {
|
||||||
|
return get(ctx, actionKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithAction(ctx context.Context, streamdeckAction string) context.Context {
|
||||||
|
return context.WithValue(ctx, actionKey, streamdeckAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
func get(ctx context.Context, key keyType) string {
|
||||||
|
if ctx == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val := ctx.Value(key)
|
||||||
|
if val == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
valStr, ok := val.(string)
|
||||||
|
if !ok {
|
||||||
|
panic("found non-string in context")
|
||||||
|
}
|
||||||
|
|
||||||
|
return valStr
|
||||||
|
}
|
53
event.go
Normal file
53
event.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package streamdeck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
sdcontext "github.com/samwho/streamdeck/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
Action string `json:"action,omitempty"`
|
||||||
|
Event string `json:"event,omitempty"`
|
||||||
|
UUID string `json:"uuid,omitempty"`
|
||||||
|
Context string `json:"context,omitempty"`
|
||||||
|
Device string `json:"device,omitempty"`
|
||||||
|
DeviceInfo DeviceInfo `json:"deviceInfo,omitempty"`
|
||||||
|
Payload json.RawMessage `json:"payload,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceInfo struct {
|
||||||
|
DeviceName string `json:"deviceName,omitempty"`
|
||||||
|
Type DeviceType `json:"type,omitempty"`
|
||||||
|
Size DeviceSize `json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceSize struct {
|
||||||
|
Columns int `json:"columns,omitempty"`
|
||||||
|
Rows int `json:"rows,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
StreamDeck DeviceType = 0
|
||||||
|
StreamDeckMini DeviceType = 1
|
||||||
|
StreamDeckXL DeviceType = 2
|
||||||
|
StreamDeckMobile DeviceType = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewEvent(ctx context.Context, name string, payload interface{}) Event {
|
||||||
|
p, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Event{
|
||||||
|
Event: name,
|
||||||
|
Action: sdcontext.Action(ctx),
|
||||||
|
Context: sdcontext.Context(ctx),
|
||||||
|
Device: sdcontext.Device(ctx),
|
||||||
|
Payload: p,
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
GO = go
|
GO = go
|
||||||
GOFLAGS =
|
GOFLAGS =
|
||||||
INSTALLDIR = "$(APPDATA)\Elgato\StreamDeck\Plugins\dev.samwho.streamdeck.livesplit.sdPlugin"
|
INSTALLDIR = "$(APPDATA)\Elgato\StreamDeck\Plugins\dev.samwho.streamdeck.counter.sdPlugin"
|
||||||
LOGDIR = "$(APPDATA)\Elgato\StreamDeck\logs"
|
LOGDIR = "$(APPDATA)\Elgato\StreamDeck\logs"
|
||||||
|
|
||||||
.PHONY: test install build
|
.PHONY: test install build logs
|
||||||
|
|
||||||
build:
|
build:
|
||||||
$(GO) build $(GOFLAGS)
|
$(GO) build $(GOFLAGS)
|
||||||
@ -14,9 +14,8 @@ test:
|
|||||||
install: build
|
install: build
|
||||||
rm -rf $(INSTALLDIR)
|
rm -rf $(INSTALLDIR)
|
||||||
mkdir $(INSTALLDIR)
|
mkdir $(INSTALLDIR)
|
||||||
cp *.png $(INSTALLDIR)
|
|
||||||
cp *.json $(INSTALLDIR)
|
cp *.json $(INSTALLDIR)
|
||||||
cp *.exe $(INSTALLDIR)
|
cp *.exe $(INSTALLDIR)
|
||||||
|
|
||||||
logs:
|
logs:
|
||||||
tail -f $(LOGDIR)/streamdeck-livesplit.log
|
tail -f $(LOGDIR)/counter.log
|
112
examples/counter/main.go
Normal file
112
examples/counter/main.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/samwho/streamdeck"
|
||||||
|
"github.com/samwho/streamdeck/payload"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
logFile = "C:\\Users\\samwh\\AppData\\Roaming\\Elgato\\StreamDeck\\logs\\streamdeck-livesplit.log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
Counter int `json:"counter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error opening file: %v", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
log.SetOutput(f)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
if err := run(ctx); err != nil {
|
||||||
|
log.Fatalf("%v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(ctx context.Context) error {
|
||||||
|
params, err := streamdeck.ParseRegistrationParams(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := streamdeck.NewClient(ctx, params)
|
||||||
|
setupCounter(client)
|
||||||
|
|
||||||
|
return client.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupCounter(client *streamdeck.Client) {
|
||||||
|
action := client.Action("dev.samwho.streamdeck.counter")
|
||||||
|
settings := make(map[string]*Settings)
|
||||||
|
|
||||||
|
action.RegisterHandler(streamdeck.WillAppear, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
|
||||||
|
p := payload.WillAppear{}
|
||||||
|
if err := json.Unmarshal(event.Payload, &p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, ok := settings[event.Context]
|
||||||
|
if !ok {
|
||||||
|
s = &Settings{}
|
||||||
|
settings[event.Context] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(p.Settings, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bg, err := streamdeck.Image(background())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.SetImage(ctx, bg, payload.HardwareAndSoftware); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.SetTitle(ctx, strconv.Itoa(s.Counter), payload.HardwareAndSoftware)
|
||||||
|
})
|
||||||
|
|
||||||
|
action.RegisterHandler(streamdeck.WillDisappear, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
|
||||||
|
s, _ := settings[event.Context]
|
||||||
|
s.Counter = 0
|
||||||
|
return client.SetSettings(ctx, s)
|
||||||
|
})
|
||||||
|
|
||||||
|
action.RegisterHandler(streamdeck.KeyDown, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
|
||||||
|
s, ok := settings[event.Context]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("couldn't find settings for context %v", event.Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Counter++
|
||||||
|
if err := client.SetSettings(ctx, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.SetTitle(ctx, strconv.Itoa(s.Counter), payload.HardwareAndSoftware)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func background() image.Image {
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, 20, 20))
|
||||||
|
for x := 0; x < 20; x++ {
|
||||||
|
for y := 0; y < 20; y++ {
|
||||||
|
img.Set(x, y, color.Black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return img
|
||||||
|
}
|
@ -1,26 +1,23 @@
|
|||||||
{
|
{
|
||||||
"Actions": [
|
"Actions": [
|
||||||
{
|
{
|
||||||
"Icon": "actionIcon",
|
"Name": "Counter",
|
||||||
"Name": "LiveSplit Go",
|
|
||||||
"States": [
|
"States": [
|
||||||
{
|
{
|
||||||
"Image": "actionDefaultImage",
|
|
||||||
"TitleAlignment": "middle",
|
"TitleAlignment": "middle",
|
||||||
"FontSize": "16"
|
"FontSize": "24"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"SupportedInMultiActions": false,
|
"SupportedInMultiActions": false,
|
||||||
"Tooltip": "LiveSplit",
|
"Tooltip": "Count something!",
|
||||||
"UUID": "dev.samwho.streamdeck.livesplit"
|
"UUID": "dev.samwho.streamdeck.counter"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"SDKVersion": 2,
|
"SDKVersion": 2,
|
||||||
"Author": "Sam Rose",
|
"Author": "Sam Rose",
|
||||||
"CodePath": "streamdeck-livesplit.exe",
|
"CodePath": "counter.exe",
|
||||||
"Description": "Test",
|
"Description": "Count something!",
|
||||||
"Name": "Test",
|
"Name": "Counter",
|
||||||
"Icon": "pluginIcon",
|
|
||||||
"URL": "https://samwho.dev",
|
"URL": "https://samwho.dev",
|
||||||
"Version": "0.1",
|
"Version": "0.1",
|
||||||
"OS": [
|
"OS": [
|
21
examples/cpu/Makefile
Normal file
21
examples/cpu/Makefile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
GO = go
|
||||||
|
GOFLAGS =
|
||||||
|
INSTALLDIR = "$(APPDATA)\Elgato\StreamDeck\Plugins\dev.samwho.streamdeck.cpu.sdPlugin"
|
||||||
|
LOGDIR = "$(APPDATA)\Elgato\StreamDeck\logs"
|
||||||
|
|
||||||
|
.PHONY: test install build logs
|
||||||
|
|
||||||
|
build:
|
||||||
|
$(GO) build $(GOFLAGS)
|
||||||
|
|
||||||
|
test:
|
||||||
|
$(GO) run $(GOFLAGS) main.go -port 12345 -pluginUUID 213 -registerEvent test -info "{\"application\":{\"language\":\"en\",\"platform\":\"mac\",\"version\":\"4.1.0\"},\"plugin\":{\"version\":\"1.1\"},\"devicePixelRatio\":2,\"devices\":[{\"id\":\"55F16B35884A859CCE4FFA1FC8D3DE5B\",\"name\":\"Device Name\",\"size\":{\"columns\":5,\"rows\":3},\"type\":0},{\"id\":\"B8F04425B95855CF417199BCB97CD2BB\",\"name\":\"Another Device\",\"size\":{\"columns\":3,\"rows\":2},\"type\":1}]}"
|
||||||
|
|
||||||
|
install: build
|
||||||
|
rm -rf $(INSTALLDIR)
|
||||||
|
mkdir $(INSTALLDIR)
|
||||||
|
cp *.json $(INSTALLDIR)
|
||||||
|
cp *.exe $(INSTALLDIR)
|
||||||
|
|
||||||
|
logs:
|
||||||
|
tail -f $(LOGDIR)/cpu.log
|
118
examples/cpu/main.go
Normal file
118
examples/cpu/main.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/samwho/streamdeck/payload"
|
||||||
|
|
||||||
|
"github.com/samwho/streamdeck"
|
||||||
|
sdcontext "github.com/samwho/streamdeck/context"
|
||||||
|
"github.com/shirou/gopsutil/cpu"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
logFile = "C:\\Users\\samwh\\AppData\\Roaming\\Elgato\\StreamDeck\\logs\\cpu.log"
|
||||||
|
|
||||||
|
imgX = 72
|
||||||
|
imgY = 72
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error opening file: %v", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
log.SetOutput(f)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
if err := run(ctx); err != nil {
|
||||||
|
log.Fatalf("%v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(ctx context.Context) error {
|
||||||
|
params, err := streamdeck.ParseRegistrationParams(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := streamdeck.NewClient(ctx, params)
|
||||||
|
setup(client)
|
||||||
|
|
||||||
|
return client.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup(client *streamdeck.Client) {
|
||||||
|
action := client.Action("dev.samwho.streamdeck.cpu")
|
||||||
|
|
||||||
|
contexts := make(map[string]struct{})
|
||||||
|
|
||||||
|
action.RegisterHandler(streamdeck.WillAppear, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
|
||||||
|
contexts[event.Context] = struct{}{}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
action.RegisterHandler(streamdeck.WillDisappear, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
|
||||||
|
delete(contexts, event.Context)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
readings := make([]float64, imgX, imgX)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for range time.Tick(time.Second) {
|
||||||
|
for i := 0; i < imgX-1; i++ {
|
||||||
|
readings[i] = readings[i+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := cpu.Percent(0, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error getting CPU reading: %v\n", err)
|
||||||
|
}
|
||||||
|
readings[imgX-1] = r[0]
|
||||||
|
|
||||||
|
for ctxStr := range contexts {
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = sdcontext.WithContext(ctx, ctxStr)
|
||||||
|
|
||||||
|
img, err := streamdeck.Image(graph(readings))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error creating image: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.SetImage(ctx, img, payload.HardwareAndSoftware); err != nil {
|
||||||
|
log.Printf("error setting image: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.SetTitle(ctx, fmt.Sprintf("CPU\n%d%%", int(r[0])), payload.HardwareAndSoftware); err != nil {
|
||||||
|
log.Printf("error setting title: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func graph(readings []float64) image.Image {
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, imgX, imgY))
|
||||||
|
for x := 0; x < imgX; x++ {
|
||||||
|
reading := readings[x] / 100
|
||||||
|
upto := int(float64(imgY) * reading)
|
||||||
|
for y := 0; y < upto; y++ {
|
||||||
|
img.Set(x, imgY-y, color.RGBA{R: 255, A: 255})
|
||||||
|
}
|
||||||
|
for y := upto; y < imgY; y++ {
|
||||||
|
img.Set(x, imgY-y, color.Black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return img
|
||||||
|
}
|
37
examples/cpu/manifest.json
Normal file
37
examples/cpu/manifest.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"Actions": [
|
||||||
|
{
|
||||||
|
"Name": "CPU graph",
|
||||||
|
"States": [
|
||||||
|
{
|
||||||
|
"TitleAlignment": "middle",
|
||||||
|
"FontSize": "14"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"SupportedInMultiActions": false,
|
||||||
|
"Tooltip": "Show a pretty little CPU graph",
|
||||||
|
"UUID": "dev.samwho.streamdeck.cpu"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"SDKVersion": 2,
|
||||||
|
"Author": "Sam Rose",
|
||||||
|
"CodePath": "cpu.exe",
|
||||||
|
"Description": "Show a pretty little CPU graph",
|
||||||
|
"Name": "CPU graph",
|
||||||
|
"URL": "https://samwho.dev",
|
||||||
|
"Version": "0.1",
|
||||||
|
"OS": [
|
||||||
|
{
|
||||||
|
"Platform": "mac",
|
||||||
|
"MinimumVersion" : "10.11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Platform": "windows",
|
||||||
|
"MinimumVersion" : "10"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Software":
|
||||||
|
{
|
||||||
|
"MinimumVersion" : "4.1"
|
||||||
|
}
|
||||||
|
}
|
33
image.go
Normal file
33
image.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package streamdeck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Image(i image.Image) (string, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
bw := bufio.NewWriter(&b)
|
||||||
|
if _, err := bw.WriteString("data:image/png;base64,"); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
w := base64.NewEncoder(base64.StdEncoding, bw)
|
||||||
|
if err := png.Encode(w, i); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.Close(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bw.Flush(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String(), nil
|
||||||
|
}
|
65
main.go
65
main.go
@ -1,65 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/samwho/streamdeck-livesplit/streamdeck"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
logFile = "C:\\Users\\samwh\\AppData\\Roaming\\Elgato\\StreamDeck\\logs\\streamdeck-livesplit.log"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
port = flag.Int("port", -1, "")
|
|
||||||
pluginUUID = flag.String("pluginUUID", "", "")
|
|
||||||
registerEvent = flag.String("registerEvent", "", "")
|
|
||||||
info = flag.String("info", "", "")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
f, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error opening file: %v", err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
log.SetOutput(f)
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
params := streamdeck.RegistrationParams{
|
|
||||||
Port: *port,
|
|
||||||
PluginUUID: *pluginUUID,
|
|
||||||
RegisterEvent: *registerEvent,
|
|
||||||
Info: *info,
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("registration params: %v\n", params)
|
|
||||||
ctx := context.Background()
|
|
||||||
if err := run(ctx, params); err != nil {
|
|
||||||
log.Fatalf("%v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(ctx context.Context, params streamdeck.RegistrationParams) error {
|
|
||||||
client, err := streamdeck.NewClient(ctx, params)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
counter := 0
|
|
||||||
client.RegisterHandler(streamdeck.KeyDown, func(ctx context.Context, client *streamdeck.Client, event streamdeck.Event) error {
|
|
||||||
counter++
|
|
||||||
log.Printf("key down! counter: %d\n", counter)
|
|
||||||
return client.SetTitle(ctx, strconv.Itoa(counter), streamdeck.HardwareAndSoftware)
|
|
||||||
})
|
|
||||||
|
|
||||||
log.Println("waiting for connection to close...")
|
|
||||||
client.Join()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
110
payload/payload.go
Normal file
110
payload/payload.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package payload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Target int
|
||||||
|
|
||||||
|
const (
|
||||||
|
HardwareAndSoftware Target = 0
|
||||||
|
OnlyHardware Target = 1
|
||||||
|
OnlySoftware Target = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogMessage struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenURL struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetTitle struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Target Target `json:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetImage struct {
|
||||||
|
Base64Image string `json:"image"`
|
||||||
|
Target Target `json:"target"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetState struct {
|
||||||
|
State int `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SwitchProfile struct {
|
||||||
|
Profile string `json:"profile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DidReceiveSettings struct {
|
||||||
|
Settings json.RawMessage `json:"settings,omitempty"`
|
||||||
|
Coordinates Coordinates `json:"coordinates,omitempty"`
|
||||||
|
IsInMultiAction bool `json:"isInMultiAction,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Coordinates struct {
|
||||||
|
Column int `json:"column,omitempty"`
|
||||||
|
Row int `json:"row,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DidReceiveGlobalSettings struct {
|
||||||
|
Settings json.RawMessage `json:"settings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyDown struct {
|
||||||
|
Settings json.RawMessage `json:"settings,omitempty"`
|
||||||
|
Coordinates Coordinates `json:"coordinates,omitempty"`
|
||||||
|
State int `json:"state,omitempty"`
|
||||||
|
UserDesiredState int `json:"userDesiredState,omitempty"`
|
||||||
|
IsInMultiAction bool `json:"isInMultiAction,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyUp struct {
|
||||||
|
Settings json.RawMessage `json:"settings,omitempty"`
|
||||||
|
Coordinates Coordinates `json:"coordinates,omitempty"`
|
||||||
|
State int `json:"state,omitempty"`
|
||||||
|
UserDesiredState int `json:"userDesiredState,omitempty"`
|
||||||
|
IsInMultiAction bool `json:"isInMultiAction,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WillAppear struct {
|
||||||
|
Settings json.RawMessage `json:"settings,omitempty"`
|
||||||
|
Coordinates Coordinates `json:"coordinates,omitempty"`
|
||||||
|
State int `json:"state,omitempty"`
|
||||||
|
IsInMultiAction bool `json:"isInMultiAction,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WillDisappear struct {
|
||||||
|
Settings json.RawMessage `json:"settings,omitempty"`
|
||||||
|
Coordinates Coordinates `json:"coordinates,omitempty"`
|
||||||
|
State int `json:"state,omitempty"`
|
||||||
|
IsInMultiAction bool `json:"isInMultiAction,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TitleParametersDidChange struct {
|
||||||
|
Settings json.RawMessage `json:"settings,omitempty"`
|
||||||
|
Coordinates Coordinates `json:"coordinates,omitempty"`
|
||||||
|
State int `json:"state,omitempty"`
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
TitleParameters TitleParameters `json:"titleParameters,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TitleParameters struct {
|
||||||
|
FontFamily string `json:"fontFamily,omitempty"`
|
||||||
|
FontSize int `json:"fontSize,omitempty"`
|
||||||
|
FontStyle string `json:"fontStyle,omitempty"`
|
||||||
|
FontUnderline bool `json:"fontUnderline,omitempty"`
|
||||||
|
ShowTitle bool `json:"showTitle,omitempty"`
|
||||||
|
TitleAlignment string `json:"titleAlignment,omitempty"`
|
||||||
|
TitleColor string `json:"titleColor,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApplicationDidLaunch struct {
|
||||||
|
Application string `json:"application,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApplicationDidTerminate struct {
|
||||||
|
Application string `json:"application,omitempty"`
|
||||||
|
}
|
46
registration.go
Normal file
46
registration.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package streamdeck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RegistrationParams struct {
|
||||||
|
Port int
|
||||||
|
PluginUUID string
|
||||||
|
RegisterEvent string
|
||||||
|
Info string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseRegistrationParams(args []string) (RegistrationParams, error) {
|
||||||
|
f := flag.NewFlagSet("registration_params", flag.ContinueOnError)
|
||||||
|
|
||||||
|
port := f.Int("port", -1, "")
|
||||||
|
pluginUUID := f.String("pluginUUID", "", "")
|
||||||
|
registerEvent := f.String("registerEvent", "", "")
|
||||||
|
info := f.String("info", "", "")
|
||||||
|
|
||||||
|
if err := f.Parse(args[1:]); err != nil {
|
||||||
|
return RegistrationParams{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if *port == -1 {
|
||||||
|
return RegistrationParams{}, fmt.Errorf("missing -port flag")
|
||||||
|
}
|
||||||
|
if *pluginUUID == "" {
|
||||||
|
return RegistrationParams{}, fmt.Errorf("missing -pluginUUID flag")
|
||||||
|
}
|
||||||
|
if *registerEvent == "" {
|
||||||
|
return RegistrationParams{}, fmt.Errorf("missing -registerEvent flag")
|
||||||
|
}
|
||||||
|
if *info == "" {
|
||||||
|
return RegistrationParams{}, fmt.Errorf("missing -info flag")
|
||||||
|
}
|
||||||
|
|
||||||
|
return RegistrationParams{
|
||||||
|
Port: *port,
|
||||||
|
PluginUUID: *pluginUUID,
|
||||||
|
RegisterEvent: *registerEvent,
|
||||||
|
Info: *info,
|
||||||
|
}, nil
|
||||||
|
}
|
@ -1,155 +0,0 @@
|
|||||||
package streamdeck
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
)
|
|
||||||
|
|
||||||
type EventHandler func(ctx context.Context, client *Client, event Event) error
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
c *websocket.Conn
|
|
||||||
handlers map[string][]EventHandler
|
|
||||||
done chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClient(ctx context.Context, params RegistrationParams) (*Client, error) {
|
|
||||||
interrupt := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(interrupt, os.Interrupt)
|
|
||||||
|
|
||||||
u := url.URL{Scheme: "ws", Host: fmt.Sprintf("127.0.0.1:%d", params.Port)}
|
|
||||||
log.Printf("connecting to StreamDeck at %v\n", u)
|
|
||||||
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
log.Printf("connected to StreamDeck\n")
|
|
||||||
|
|
||||||
done := make(chan struct{})
|
|
||||||
|
|
||||||
client := &Client{
|
|
||||||
c: c,
|
|
||||||
handlers: make(map[string][]EventHandler),
|
|
||||||
done: done,
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer close(done)
|
|
||||||
log.Println("starting read loop")
|
|
||||||
for {
|
|
||||||
messageType, message, err := client.c.ReadMessage()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("read error: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if messageType == websocket.PingMessage {
|
|
||||||
log.Printf("received ping message\n")
|
|
||||||
if err := client.c.WriteMessage(websocket.PongMessage, []byte{}); err != nil {
|
|
||||||
log.Printf("error while ponging: %v\n", err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if messageType == websocket.CloseMessage {
|
|
||||||
// handle close message
|
|
||||||
panic("websocket close!")
|
|
||||||
}
|
|
||||||
|
|
||||||
event := Event{}
|
|
||||||
if err := json.Unmarshal(message, &event); err != nil {
|
|
||||||
log.Printf("failed to unmarshal received event: %s\n", string(message))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("recv: ", string(message))
|
|
||||||
|
|
||||||
ctx := setContext(ctx, event.Context)
|
|
||||||
for _, f := range client.handlers[event.Event] {
|
|
||||||
if err := f(ctx, client, event); err != nil {
|
|
||||||
log.Printf("error in handler for event %v: %v\n", event.Event, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err := client.register(params); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) register(params RegistrationParams) error {
|
|
||||||
log.Println("sending register event...")
|
|
||||||
if err := client.send(Event{UUID: params.PluginUUID, Event: params.RegisterEvent}); err != nil {
|
|
||||||
client.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) send(event Event) error {
|
|
||||||
j, _ := json.Marshal(event)
|
|
||||||
log.Printf("sending message: %v\n", string(j))
|
|
||||||
return client.c.WriteJSON(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) SetSettings(ctx context.Context, settings interface{}) error {
|
|
||||||
return client.send(NewEvent(ctx, SetSettings, settings))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) GetSettings(ctx context.Context) error {
|
|
||||||
return client.send(NewEvent(ctx, GetSettings, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) SetGlobalSettings(ctx context.Context, settings interface{}) error {
|
|
||||||
return client.send(NewEvent(ctx, SetGlobalSettings, settings))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) GetGlobalSettings(ctx context.Context) error {
|
|
||||||
return client.send(NewEvent(ctx, GetGlobalSettings, nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) OpenURL(ctx context.Context, u url.URL) error {
|
|
||||||
return client.send(NewEvent(ctx, OpenURL, OpenURLPayload{URL: u.String()}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) LogMessage(message string) error {
|
|
||||||
return client.send(NewEvent(nil, LogMessage, LogMessagePayload{Message: message}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) SetTitle(ctx context.Context, title string, target Target) error {
|
|
||||||
return client.send(NewEvent(ctx, SetTitle, SetTitlePayload{Title: title, Target: target}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) SetImage(ctx context.Context, base64image string, target Target) error {
|
|
||||||
return client.send(NewEvent(ctx, SetImage, SetImagePayload{Base64Image: base64image, Target: target}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) RegisterHandler(eventName string, handler EventHandler) {
|
|
||||||
client.handlers[eventName] = append(client.handlers[eventName], handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) Close() error {
|
|
||||||
err := client.c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-client.done:
|
|
||||||
case <-time.After(time.Second):
|
|
||||||
}
|
|
||||||
return client.c.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) Join() {
|
|
||||||
<-client.done
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
package streamdeck
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Event struct {
|
|
||||||
Action string `json:"action,omitempty"`
|
|
||||||
Event string `json:"event,omitempty"`
|
|
||||||
UUID string `json:"uuid,omitempty"`
|
|
||||||
Context string `json:"context,omitempty"`
|
|
||||||
Device string `json:"device,omitempty"`
|
|
||||||
Payload interface{} `json:"payload,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEvent(ctx context.Context, name string, payload interface{}) Event {
|
|
||||||
return Event{
|
|
||||||
Event: name,
|
|
||||||
Context: getContext(ctx),
|
|
||||||
Payload: payload,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type LogMessagePayload struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type OpenURLPayload struct {
|
|
||||||
URL string `json:"url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Target int
|
|
||||||
|
|
||||||
const (
|
|
||||||
HardwareAndSoftware Target = 0
|
|
||||||
OnlyHardware Target = 1
|
|
||||||
OnlySoftware Target = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
type SetTitlePayload struct {
|
|
||||||
Title string `json:"title"`
|
|
||||||
Target Target `json:"target"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SetImagePayload struct {
|
|
||||||
Base64Image string `json:"image"`
|
|
||||||
Target Target `json:"target"`
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package streamdeck
|
|
||||||
|
|
||||||
type RegistrationParams struct {
|
|
||||||
Port int
|
|
||||||
PluginUUID string
|
|
||||||
RegisterEvent string
|
|
||||||
Info string
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user