Initial checkin
This commit is contained in:
parent
8cebd67505
commit
4f0a2aeac8
301
events_received.go
Normal file
301
events_received.go
Normal file
@ -0,0 +1,301 @@
|
||||
package streamdeck
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var receivedEventTypeMap = map[string]reflect.Type{}
|
||||
|
||||
func init() {
|
||||
receivedEventTypeMap["keyUp"] = reflect.TypeOf(ERKeyUp{})
|
||||
receivedEventTypeMap["didReceiveSettingsPayload"] = reflect.TypeOf(ERDidReceiveSettingsPayload{})
|
||||
receivedEventTypeMap["didReceiveSettings"] = reflect.TypeOf(ERDidReceiveSettings{})
|
||||
receivedEventTypeMap["globalSettings"] = reflect.TypeOf(ERGlobalSettings{})
|
||||
receivedEventTypeMap["didReceiveDeepLink"] = reflect.TypeOf(ERDidReceiveDeepLink{})
|
||||
receivedEventTypeMap["touchTap"] = reflect.TypeOf(ERTouchTap{})
|
||||
receivedEventTypeMap["dialDown"] = reflect.TypeOf(ERDialDown{})
|
||||
receivedEventTypeMap["dialUp"] = reflect.TypeOf(ERDialUp{})
|
||||
receivedEventTypeMap["dialRotate"] = reflect.TypeOf(ERDialRotate{})
|
||||
receivedEventTypeMap["keyDown"] = reflect.TypeOf(ERKeyDown{})
|
||||
receivedEventTypeMap["willAppear"] = reflect.TypeOf(ERWillAppear{})
|
||||
receivedEventTypeMap["willDisappear"] = reflect.TypeOf(ERWillDisappear{})
|
||||
receivedEventTypeMap["titleParametersDidChange"] = reflect.TypeOf(ERTitleParametersDidChange{})
|
||||
receivedEventTypeMap["deviceDidConnect"] = reflect.TypeOf(ERDeviceDidConnect{})
|
||||
receivedEventTypeMap["deviceDidDisconnect"] = reflect.TypeOf(ERDeviceDidDisconnect{})
|
||||
receivedEventTypeMap["applicationDidLaunch"] = reflect.TypeOf(ERApplicationDidLaunch{})
|
||||
receivedEventTypeMap["applicationDidTerminate"] = reflect.TypeOf(ERApplicationDidTerminate{})
|
||||
receivedEventTypeMap["applicationSystemDidWakeUp"] = reflect.TypeOf(ERApplicationSystemDidWakeUp{})
|
||||
receivedEventTypeMap["applicationPropertyInspectorDidAppear"] = reflect.TypeOf(ERApplicationPropertyInspectorDidAppear{})
|
||||
receivedEventTypeMap["applicationPropertyInspectorDidDisappear"] = reflect.TypeOf(ERApplicationPropertyInspectorDidDisappear{})
|
||||
receivedEventTypeMap["applicationPropertySendToPlugin"] = reflect.TypeOf(ERApplicationPropertySendToPlugin{})
|
||||
receivedEventTypeMap["applicationPropertySendToPropertyInspector"] = reflect.TypeOf(ERApplicationPropertySendToPropertyInspector{})
|
||||
}
|
||||
|
||||
type ERBase struct {
|
||||
Event string `json:"event"`
|
||||
}
|
||||
|
||||
//{Action: Event:deviceDidConnect Context: Device:A1DA463F033AD2616E05636CD16F064F Payload:[]}"}
|
||||
|
||||
type ERCommon struct {
|
||||
Action string `json:"action"`
|
||||
Event string `json:"event"`
|
||||
Context string `json:"context"`
|
||||
Device string `json:"device"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#didreceivesettings
|
||||
|
||||
type ERDidReceiveSettingsPayload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
IsInMultiAction bool `json:"isInMultiAction"`
|
||||
}
|
||||
|
||||
type ERDidReceiveSettings struct {
|
||||
ERCommon
|
||||
Payload ERDidReceiveSettingsPayload `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#didreceiveglobalsettings
|
||||
|
||||
type ERGlobalSettings struct {
|
||||
Event string `json:"event"`
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#didreceivedeeplink
|
||||
|
||||
type ERDidReceiveDeepLink struct {
|
||||
Event string `json:"event"`
|
||||
Payload struct {
|
||||
Url string `json:"url"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#touchtap-sd
|
||||
|
||||
type ERTouchTap struct {
|
||||
ERCommon
|
||||
Payload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Controller string `json:"controller"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
TapPosition []int `json:"tapPos"`
|
||||
Hold bool `json:"hold"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#dialdown-sd
|
||||
|
||||
type ERDialDown struct {
|
||||
ERCommon
|
||||
Payload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Controller string `json:"controller"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#dialup-sd
|
||||
|
||||
type ERDialUp struct {
|
||||
ERCommon
|
||||
Payload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Controller string `json:"controller"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#dialrotate-sd
|
||||
|
||||
type ERDialRotate struct {
|
||||
ERCommon
|
||||
Payload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Controller string `json:"controller"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
Ticks int `json:"ticks"`
|
||||
Pressed bool `json:"pressed"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#keydown
|
||||
|
||||
type ERKeyDown struct {
|
||||
ERCommon
|
||||
Payload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
State int `json:"state"`
|
||||
UserDesiredState int `json:"userDesiredState"`
|
||||
IsInMultiAction bool `json:"isInMultiAction"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#keyup
|
||||
type ERKeyUp struct {
|
||||
ERCommon
|
||||
Payload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
State int `json:"state"`
|
||||
UserDesiredState int `json:"userDesiredState"`
|
||||
IsInMultiAction bool `json:"isInMultiAction"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#willappear
|
||||
type ERWillAppear struct {
|
||||
ERCommon
|
||||
Payload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
Controller string `json:"controller"`
|
||||
State int `json:"state"`
|
||||
IsInMultiAction bool `json:"isInMultiAction"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#willdisappear
|
||||
|
||||
const ERWillDisappearAction = "willDisappear"
|
||||
|
||||
type ERWillDisappear struct {
|
||||
ERCommon
|
||||
Payload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
Controller string `json:"controller"`
|
||||
State int `json:"state"`
|
||||
IsInMultiAction bool `json:"isInMultiAction"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#titleparametersdidchange
|
||||
|
||||
type ERTitleParametersDidChange struct {
|
||||
ERCommon
|
||||
Payload struct {
|
||||
Settings json.RawMessage `json:"settings"`
|
||||
Coordinates struct {
|
||||
Column int `json:"column"`
|
||||
Row int `json:"row"`
|
||||
} `json:"coordinates"`
|
||||
State int `json:"state"`
|
||||
Title string `json:"title"`
|
||||
TitleParameters struct {
|
||||
FontFamily string `json:"fontFamily"`
|
||||
FontSize int `json:"fontSize"`
|
||||
FontStyle string `json:"fontStyle"`
|
||||
FontUnderline bool `json:"fontUnderline"`
|
||||
ShowTitle bool `json:"showTitle"`
|
||||
TitleAlignment string `json:"titleAlignment"`
|
||||
TitleColor string `json:"titleColor"`
|
||||
} `json:"titleParameters"`
|
||||
} `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#devicedidconnect
|
||||
|
||||
type ERDeviceDidConnect struct {
|
||||
Event string `json:"event"`
|
||||
|
||||
Device string `json:"device"`
|
||||
DeviceInfo struct {
|
||||
Name string `json:"name"`
|
||||
DeviceType int `json:"type"`
|
||||
Size struct {
|
||||
Columns int `json:"columns"`
|
||||
Rows int `json:"rows"`
|
||||
} `json:"size"`
|
||||
} `json:"deviceInfo"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#devicediddisconnect
|
||||
|
||||
type ERDeviceDidDisconnect struct {
|
||||
Event string `json:"event"`
|
||||
Device string `json:"device"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#applicationdidlaunch
|
||||
|
||||
type ERApplicationDidLaunch struct {
|
||||
Event string `json:"event"`
|
||||
Payload struct {
|
||||
Application string `json:"application"`
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#applicationdidterminate
|
||||
|
||||
type ERApplicationDidTerminate struct {
|
||||
Event string `json:"event"`
|
||||
Payload struct {
|
||||
Application string `json:"application"`
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#systemdidwakeup
|
||||
|
||||
type ERApplicationSystemDidWakeUp struct {
|
||||
Event string `json:"event"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#propertyinspectordidappear
|
||||
type ERApplicationPropertyInspectorDidAppear struct {
|
||||
ERCommon
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#propertyinspectordiddisappear
|
||||
|
||||
type ERApplicationPropertyInspectorDidDisappear struct {
|
||||
ERCommon
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#sendtoplugin
|
||||
|
||||
type ERApplicationPropertySendToPlugin struct {
|
||||
Action string `json:"action"`
|
||||
Event string `json:"event"`
|
||||
Context string `json:"context"`
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-received#sendtopropertyinspector
|
||||
type ERApplicationPropertySendToPropertyInspector struct {
|
||||
Action string `json:"action"`
|
||||
Event string `json:"event"`
|
||||
Context string `json:"context"`
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
}
|
385
events_send.go
Normal file
385
events_send.go
Normal file
@ -0,0 +1,385 @@
|
||||
package streamdeck
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"image"
|
||||
"image/png"
|
||||
)
|
||||
|
||||
type EventTarget int
|
||||
|
||||
const EventTargetBoth = 0
|
||||
const EventTargetHardware = 1
|
||||
const EventTargetSoftware = 2
|
||||
|
||||
type ESCommon struct {
|
||||
Event string `json:"event"`
|
||||
Context string `json:"context"`
|
||||
}
|
||||
|
||||
type ESCommonNoContext struct {
|
||||
Event string `json:"event"`
|
||||
}
|
||||
|
||||
type ESOpenMessage struct {
|
||||
ESCommonNoContext
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#setsettings
|
||||
|
||||
type ESSetSettings struct {
|
||||
ESCommon
|
||||
Payload json.RawMessage
|
||||
}
|
||||
|
||||
func NewESSetSettings(context string, payload json.RawMessage) ESSetSettings {
|
||||
return ESSetSettings{
|
||||
ESCommon: ESCommon{
|
||||
Event: "setSettings",
|
||||
Context: context,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#getsettings
|
||||
|
||||
type ESGetSettings struct {
|
||||
ESCommon
|
||||
}
|
||||
|
||||
func NewESGetSettings(context string) ESGetSettings {
|
||||
return ESGetSettings{
|
||||
ESCommon: ESCommon{
|
||||
Event: "getSettings",
|
||||
Context: context,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#setglobalsettings
|
||||
|
||||
type ESSetGlobalSettings struct {
|
||||
ESCommon
|
||||
Payload json.RawMessage
|
||||
}
|
||||
|
||||
func NewESSetGlobalSettings(context string, payload json.RawMessage) ESSetGlobalSettings {
|
||||
return ESSetGlobalSettings{
|
||||
ESCommon: ESCommon{
|
||||
Event: "setGlobalSettings",
|
||||
Context: context,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#getglobalsettings
|
||||
|
||||
type ESGetGlobalSettings struct {
|
||||
ESCommon
|
||||
}
|
||||
|
||||
func NewESGetGlobalSettings(context string) ESGetGlobalSettings {
|
||||
return ESGetGlobalSettings{
|
||||
ESCommon: ESCommon{
|
||||
Event: "getGlobalSettings",
|
||||
Context: context,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#openurl
|
||||
|
||||
type ESOpenURL struct {
|
||||
ESCommonNoContext
|
||||
Payload ESOpenURLPayload `json:"payload"`
|
||||
}
|
||||
|
||||
type ESOpenURLPayload struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
func NewESOpenURL(url string) ESOpenURL {
|
||||
return ESOpenURL{
|
||||
ESCommonNoContext: ESCommonNoContext{Event: "openUrl"},
|
||||
Payload: ESOpenURLPayload{
|
||||
URL: url,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#logmessage
|
||||
type ESLogMessage struct {
|
||||
ESCommonNoContext
|
||||
Payload ESLogMessagePayload `json:"payload"`
|
||||
}
|
||||
|
||||
type ESLogMessagePayload struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func NewESLogMessage(message string) ESLogMessage {
|
||||
return ESLogMessage{
|
||||
ESCommonNoContext: ESCommonNoContext{Event: "logMessage"},
|
||||
Payload: ESLogMessagePayload{Message: message},
|
||||
}
|
||||
}
|
||||
|
||||
// setTitle https://docs.elgato.com/sdk/plugins/events-sent#settitle
|
||||
type ESSetTitle struct {
|
||||
ESCommon
|
||||
Payload ESSetTitlePayload `json:"payload"`
|
||||
}
|
||||
|
||||
type ESSetTitlePayload struct {
|
||||
Title string `json:"title"`
|
||||
Target EventTarget `json:"target"`
|
||||
State int `json:"state"`
|
||||
}
|
||||
|
||||
func NewESSetTitle(context string, title string, target EventTarget, state int) ESSetTitle {
|
||||
return ESSetTitle{
|
||||
ESCommon: ESCommon{
|
||||
Event: "setTitle",
|
||||
Context: context,
|
||||
},
|
||||
Payload: ESSetTitlePayload{
|
||||
Title: title,
|
||||
Target: target,
|
||||
State: state,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#setimage
|
||||
type ESSetImage struct {
|
||||
ESCommon
|
||||
Payload ESSetImagePayload `json:"payload"`
|
||||
}
|
||||
|
||||
type ESSetImagePayload struct {
|
||||
Image string `json:"title"`
|
||||
Target EventTarget `json:"target"`
|
||||
State int `json:"state"`
|
||||
}
|
||||
|
||||
func NewESSetImagePayload(context string, imageBase64 string, target EventTarget, state int) ESSetImage {
|
||||
return ESSetImage{
|
||||
ESCommon: ESCommon{
|
||||
Event: "setImage",
|
||||
Context: context,
|
||||
},
|
||||
Payload: ESSetImagePayload{
|
||||
Image: imageBase64,
|
||||
Target: target,
|
||||
State: state,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#setfeedback-sd
|
||||
type ESSetFeedback struct {
|
||||
ESCommon
|
||||
Payload json.RawMessage `json:"payload"`
|
||||
}
|
||||
|
||||
func NewESSetFeedback(context string, payload json.RawMessage) ESSetFeedback {
|
||||
return ESSetFeedback{
|
||||
ESCommon: ESCommon{
|
||||
Event: "setFeedback",
|
||||
Context: context,
|
||||
},
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#setfeedbacklayout-sd
|
||||
type ESSetFeedbackLayout struct {
|
||||
ESCommon
|
||||
Payload ESSetFeedbackLayoutPayload `json:"payload"`
|
||||
}
|
||||
|
||||
type ESSetFeedbackLayoutPayload struct {
|
||||
Layout string `json:"layout"`
|
||||
}
|
||||
|
||||
func NewESSetFeedbackLayout(context string, layout string) ESSetFeedbackLayout {
|
||||
return ESSetFeedbackLayout{
|
||||
ESCommon: ESCommon{
|
||||
Event: "setFeedbackLayout",
|
||||
Context: context,
|
||||
},
|
||||
Payload: ESSetFeedbackLayoutPayload{
|
||||
Layout: layout,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#settriggerdescription-sd
|
||||
type ESSetTriggerDescription struct {
|
||||
ESCommon
|
||||
Payload ESSetTriggerDescriptionPayload `json:"payload"`
|
||||
}
|
||||
|
||||
type ESSetTriggerDescriptionPayload struct {
|
||||
Rotate string `json:"rotate,omitempty"`
|
||||
Push string `json:"push,omitempty"`
|
||||
Touch string `json:"touch,omitempty"`
|
||||
LongTouch string `json:"longTouch,omitempty"`
|
||||
}
|
||||
|
||||
func NewESSetTriggerDescription(context string, rotate, push, touch, longTouch string) ESSetTriggerDescription {
|
||||
return ESSetTriggerDescription{
|
||||
ESCommon: ESCommon{
|
||||
Event: "setTriggerDescription",
|
||||
Context: context,
|
||||
},
|
||||
Payload: ESSetTriggerDescriptionPayload{
|
||||
Rotate: rotate,
|
||||
Push: push,
|
||||
Touch: touch,
|
||||
LongTouch: longTouch,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#showalert
|
||||
type ESShowAlert struct {
|
||||
ESCommon
|
||||
}
|
||||
|
||||
func NewESShowAlert(context string) ESShowAlert {
|
||||
return ESShowAlert{
|
||||
ESCommon: ESCommon{
|
||||
Event: "showAlert",
|
||||
Context: context,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#showok
|
||||
type ESShowOK struct {
|
||||
ESCommon
|
||||
}
|
||||
|
||||
func NewESShowOK(context string) ESShowOK {
|
||||
return ESShowOK{
|
||||
ESCommon: ESCommon{
|
||||
Event: "showOk",
|
||||
Context: context,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//https://docs.elgato.com/sdk/plugins/events-sent#setstate
|
||||
|
||||
type ESSetState struct {
|
||||
ESCommon
|
||||
Payload ESSetStatePayload `json:"payload"`
|
||||
}
|
||||
|
||||
type ESSetStatePayload struct {
|
||||
State int `json:"state"`
|
||||
}
|
||||
|
||||
func NewESSetState(context string, state int) ESSetImage {
|
||||
return ESSetImage{
|
||||
ESCommon: ESCommon{
|
||||
Event: "setState",
|
||||
Context: context,
|
||||
},
|
||||
Payload: ESSetImagePayload{
|
||||
State: state,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#switchtoprofile
|
||||
|
||||
type ESSwitchToProfile struct {
|
||||
ESCommon
|
||||
Device string `json:"device"`
|
||||
Payload ESSwitchToProfilePayload `json:"payload"`
|
||||
}
|
||||
|
||||
type ESSwitchToProfilePayload struct {
|
||||
Profile string `json:"profile"`
|
||||
Page int `json:"page"`
|
||||
}
|
||||
|
||||
func NewESSwitchToProfile(context string, device, profileName string, page int) ESSwitchToProfile {
|
||||
return ESSwitchToProfile{
|
||||
ESCommon: ESCommon{
|
||||
Event: "switchToProfile",
|
||||
Context: context,
|
||||
},
|
||||
Device: device,
|
||||
Payload: ESSwitchToProfilePayload{
|
||||
Profile: profileName,
|
||||
Page: page,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#sendtopropertyinspector
|
||||
|
||||
type ESSendToPropertyInspector struct {
|
||||
ESCommon
|
||||
Action string `json:"action"`
|
||||
Payload json.RawMessage
|
||||
}
|
||||
|
||||
func NewESSendToPropertyInspector(context string, action string, payload json.RawMessage) ESSendToPropertyInspector {
|
||||
return ESSendToPropertyInspector{
|
||||
ESCommon: ESCommon{
|
||||
Event: "sendToPropertyInspector",
|
||||
Context: context,
|
||||
},
|
||||
Action: action,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.elgato.com/sdk/plugins/events-sent#sendtoplugin
|
||||
|
||||
type ESSendToPlugin struct {
|
||||
ESCommon
|
||||
Action string `json:"action"`
|
||||
Payload json.RawMessage
|
||||
}
|
||||
|
||||
func NewESSendToPlugin(context string, action string, payload json.RawMessage) ESSendToPlugin {
|
||||
return ESSendToPlugin{
|
||||
ESCommon: ESCommon{
|
||||
Event: "sendToPlugin",
|
||||
Context: context,
|
||||
},
|
||||
Action: action,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
/// --------- helpers ----------
|
||||
|
||||
// Turns an image.Image into a string suitable for delivering
|
||||
// via a ESSetImage struct
|
||||
func ImageToPayload(i image.Image) string {
|
||||
|
||||
out := bytes.Buffer{}
|
||||
b64 := base64.NewEncoder(base64.RawStdEncoding, &out)
|
||||
err := png.Encode(b64, i)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return "data:image/png;base64," + out.String()
|
||||
}
|
||||
|
||||
// Turns an image.Image into a string suitable for delivering
|
||||
// via a ESSetImage struct
|
||||
func SVGToPayload(context string, svg string) string {
|
||||
return "data:image/svg+xml;charset=utf8," + svg
|
||||
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module github.com/tardisx/streamdeck-plugin
|
||||
|
||||
go 1.22.1
|
||||
|
||||
require github.com/gorilla/websocket v1.5.3
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
202
streamdeck.go
Normal file
202
streamdeck.go
Normal file
@ -0,0 +1,202 @@
|
||||
package streamdeck
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
type logger interface {
|
||||
Info(string, ...any)
|
||||
Error(string, ...any)
|
||||
Debug(string, ...any)
|
||||
}
|
||||
|
||||
type nullLogger struct{}
|
||||
|
||||
func (nl nullLogger) Info(string, ...any) {}
|
||||
func (nl nullLogger) Error(string, ...any) {}
|
||||
func (nl nullLogger) Debug(string, ...any) {}
|
||||
|
||||
type EventHandler struct {
|
||||
MsgType string
|
||||
Handler func()
|
||||
}
|
||||
|
||||
type Connection struct {
|
||||
ws *websocket.Conn
|
||||
logger logger
|
||||
handlers map[reflect.Type]reflect.Value
|
||||
done chan (bool)
|
||||
}
|
||||
|
||||
func New() Connection {
|
||||
return Connection{
|
||||
handlers: make(map[reflect.Type]reflect.Value),
|
||||
logger: nullLogger{},
|
||||
done: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
func NewWithLogger(l logger) Connection {
|
||||
c := New()
|
||||
c.logger = l
|
||||
return c
|
||||
}
|
||||
|
||||
func (conn *Connection) Connect(port int, openEvent string, uuid string) error {
|
||||
|
||||
c, _, err := websocket.DefaultDialer.Dial(fmt.Sprintf("ws://localhost:%d", port), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn.ws = c
|
||||
msg := ESOpenMessage{
|
||||
ESCommonNoContext: ESCommonNoContext{
|
||||
Event: openEvent,
|
||||
},
|
||||
UUID: uuid,
|
||||
}
|
||||
conn.logger.Debug(fmt.Sprintf("writing openMessage: %+v", msg))
|
||||
err = c.WriteJSON(msg)
|
||||
if err != nil {
|
||||
conn.logger.Error(err.Error())
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// run the reader forever
|
||||
conn.logger.Info("starting reader")
|
||||
go conn.reader()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Connection) WaitForPluginExit() {
|
||||
<-c.done
|
||||
}
|
||||
|
||||
// RegisterHandler registers a function to be called for a particular event. The
|
||||
// event to be handled is determined by the functions single parameter type.
|
||||
// This should be called before Connect to be sure your application is ready to
|
||||
// receive events. You can register as many handlers as you like, but only one
|
||||
// function per event type. This function will panic if the wrong kind of
|
||||
// function is passed in, or if you try to register more than one for a single
|
||||
// event type.
|
||||
func (r *Connection) RegisterHandler(handler any) {
|
||||
hType := reflect.TypeOf(handler)
|
||||
if hType.Kind() != reflect.Func {
|
||||
panic("handler must be a function")
|
||||
}
|
||||
// Assuming the function takes exactly one argument
|
||||
if hType.NumIn() != 1 {
|
||||
panic("handler func must take exactly one argument")
|
||||
}
|
||||
|
||||
argType := hType.In(0)
|
||||
|
||||
// check its a valid one (one that matches an event type)
|
||||
valid := false
|
||||
for i := range receivedEventTypeMap {
|
||||
if receivedEventTypeMap[i] == argType {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
panic("you cannot register a handler with this argument type")
|
||||
}
|
||||
|
||||
_, alreadyExists := r.handlers[argType]
|
||||
if alreadyExists {
|
||||
panic("handler for " + argType.Name() + " already exists")
|
||||
}
|
||||
|
||||
r.handlers[argType] = reflect.ValueOf(handler)
|
||||
}
|
||||
|
||||
func (conn *Connection) Send(e any) error {
|
||||
b, _ := json.Marshal(e)
|
||||
conn.logger.Debug(fmt.Sprintf("sending: %s", string(b)))
|
||||
|
||||
return conn.ws.WriteJSON(e)
|
||||
}
|
||||
|
||||
func (conn *Connection) handle(event any) {
|
||||
conn.logger.Debug(fmt.Sprintf("handle: incoming a %T", event))
|
||||
argType := reflect.TypeOf(event)
|
||||
handler, ok := conn.handlers[argType]
|
||||
if !ok {
|
||||
conn.logger.Debug(fmt.Sprintf("handle: no handler registered for type %s", argType))
|
||||
return
|
||||
} else {
|
||||
conn.logger.Debug(fmt.Sprintf("handle: found handler function for type %s", argType))
|
||||
|
||||
v := []reflect.Value{reflect.ValueOf(event)}
|
||||
conn.logger.Debug(fmt.Sprintf("handle: handler func: %+v", handler))
|
||||
conn.logger.Debug(fmt.Sprintf("handle: handler var: %+v", v))
|
||||
|
||||
conn.logger.Debug("handle: calling handler function")
|
||||
|
||||
handler.Call(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (conn *Connection) reader() {
|
||||
|
||||
for {
|
||||
_, r, err := conn.ws.NextReader()
|
||||
if err != nil {
|
||||
conn.logger.Error(err.Error())
|
||||
break
|
||||
}
|
||||
|
||||
b := bytes.Buffer{}
|
||||
r = io.TeeReader(r, &b)
|
||||
base := ERBase{}
|
||||
err = json.NewDecoder(r).Decode(&base)
|
||||
if err != nil {
|
||||
conn.logger.Error("cannot decode: " + err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
t, ok := receivedEventTypeMap[base.Event]
|
||||
if !ok {
|
||||
conn.logger.Error(fmt.Sprintf("no type registered for event '%s'", base.Event))
|
||||
continue
|
||||
}
|
||||
|
||||
d, err := conn.unmarshalToConcrete(t, b.Bytes())
|
||||
if err != nil {
|
||||
conn.logger.Error("cannot unmarshal: " + err.Error())
|
||||
continue
|
||||
}
|
||||
conn.handle(d)
|
||||
}
|
||||
conn.logger.Info("websocket closed, shutting down reader")
|
||||
conn.done <- true
|
||||
}
|
||||
|
||||
// unmarshalToConcrete takes a reflect.Type and a byte slice, creates a concrete
|
||||
// instance of the type and unmarshals the JSON byte slice into it.
|
||||
func (conn *Connection) unmarshalToConcrete(t reflect.Type, b []byte) (any, error) {
|
||||
// t is a reflect.Type of the thing we need to decode into
|
||||
d := reflect.New(t).Interface()
|
||||
conn.logger.Info(fmt.Sprintf("instance is a %T", d))
|
||||
|
||||
err := json.Unmarshal(b, &d)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal this:\n%s\ninto a %v (%v)\nbecause: %s", string(b), d, t, err.Error())
|
||||
}
|
||||
|
||||
// get concrete instance of d into v
|
||||
v := reflect.ValueOf(d).Elem().Interface()
|
||||
conn.logger.Info(fmt.Sprintf("NOW instance is a %T", v))
|
||||
|
||||
conn.logger.Debug(fmt.Sprintf("reader: unmarshalled to: %+v", v))
|
||||
return v, nil
|
||||
}
|
69
streamdesk_test.go
Normal file
69
streamdesk_test.go
Normal file
@ -0,0 +1,69 @@
|
||||
package streamdeck
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testLogger struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (tl testLogger) Info(s string, x ...any) { tl.t.Log(s, x) }
|
||||
func (tl testLogger) Debug(s string, x ...any) { tl.t.Log(s, x) }
|
||||
func (tl testLogger) Error(s string, x ...any) { tl.t.Log(s, x) }
|
||||
|
||||
func TestReflection(t *testing.T) {
|
||||
|
||||
c := NewWithLogger(testLogger{t: t})
|
||||
// incoming
|
||||
in := ERDidReceiveSettingsPayload{}
|
||||
|
||||
ranHandler := false
|
||||
c.RegisterHandler(func(event ERDidReceiveSettingsPayload) {
|
||||
ranHandler = true
|
||||
})
|
||||
|
||||
c.handle(in)
|
||||
|
||||
if !ranHandler {
|
||||
t.Error("did not run handler")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestUmmarshal(t *testing.T) {
|
||||
|
||||
b := []byte(`
|
||||
{
|
||||
"action": "com.elgato.example.action1",
|
||||
"event": "keyUp",
|
||||
"context": "ABC123",
|
||||
"device": "DEF456",
|
||||
"payload": {
|
||||
"settings": {},
|
||||
"coordinates": {
|
||||
"column": 3,
|
||||
"row": 1
|
||||
},
|
||||
"state": 0,
|
||||
"userDesiredState": 1,
|
||||
"isInMultiAction": false
|
||||
}
|
||||
}`)
|
||||
|
||||
c := NewWithLogger(testLogger{t: t})
|
||||
keyUp, err := c.unmarshalToConcrete(receivedEventTypeMap["keyUp"], b)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
realKeyUp, ok := keyUp.(ERKeyUp)
|
||||
if !ok {
|
||||
t.Errorf("wrong type (is %T)", keyUp)
|
||||
}
|
||||
if realKeyUp.Context != "ABC123" {
|
||||
t.Error("wrong value")
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user