mirror of https://github.com/jetkvm/kvm.git
add jsonrpc keyboard macro get/set
This commit is contained in:
parent
1f8f885a1d
commit
5650bad796
61
config.go
61
config.go
|
@ -14,6 +14,65 @@ type WakeOnLanDevice struct {
|
||||||
MacAddress string `json:"macAddress"`
|
MacAddress string `json:"macAddress"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constants for keyboard macro limits
|
||||||
|
const (
|
||||||
|
MaxMacrosPerDevice = 25
|
||||||
|
MaxStepsPerMacro = 10
|
||||||
|
MaxKeysPerStep = 10
|
||||||
|
MinStepDelay = 50
|
||||||
|
MaxStepDelay = 2000
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyboardMacroStep struct {
|
||||||
|
Keys []string `json:"keys"`
|
||||||
|
Modifiers []string `json:"modifiers"`
|
||||||
|
Delay int `json:"delay"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *KeyboardMacroStep) Validate() error {
|
||||||
|
if len(s.Keys) > MaxKeysPerStep {
|
||||||
|
return fmt.Errorf("too many keys in step (max %d)", MaxKeysPerStep)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Delay < MinStepDelay {
|
||||||
|
s.Delay = MinStepDelay
|
||||||
|
} else if s.Delay > MaxStepDelay {
|
||||||
|
s.Delay = MaxStepDelay
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyboardMacro struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Steps []KeyboardMacroStep `json:"steps"`
|
||||||
|
SortOrder int `json:"sortOrder,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *KeyboardMacro) Validate() error {
|
||||||
|
if m.Name == "" {
|
||||||
|
return fmt.Errorf("macro name cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m.Steps) == 0 {
|
||||||
|
return fmt.Errorf("macro must have at least one step")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(m.Steps) > MaxStepsPerMacro {
|
||||||
|
m.Steps = m.Steps[:MaxStepsPerMacro]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range m.Steps {
|
||||||
|
if err := m.Steps[i].Validate(); err != nil {
|
||||||
|
return fmt.Errorf("invalid step %d: %w", i+1, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
CloudURL string `json:"cloud_url"`
|
CloudURL string `json:"cloud_url"`
|
||||||
CloudAppURL string `json:"cloud_app_url"`
|
CloudAppURL string `json:"cloud_app_url"`
|
||||||
|
@ -26,6 +85,7 @@ type Config struct {
|
||||||
LocalAuthToken string `json:"local_auth_token"`
|
LocalAuthToken string `json:"local_auth_token"`
|
||||||
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
|
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
|
||||||
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
|
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
|
||||||
|
KeyboardMacros []KeyboardMacro `json:"keyboard_macros"`
|
||||||
EdidString string `json:"hdmi_edid_string"`
|
EdidString string `json:"hdmi_edid_string"`
|
||||||
ActiveExtension string `json:"active_extension"`
|
ActiveExtension string `json:"active_extension"`
|
||||||
DisplayMaxBrightness int `json:"display_max_brightness"`
|
DisplayMaxBrightness int `json:"display_max_brightness"`
|
||||||
|
@ -43,6 +103,7 @@ var defaultConfig = &Config{
|
||||||
CloudAppURL: "https://app.jetkvm.com",
|
CloudAppURL: "https://app.jetkvm.com",
|
||||||
AutoUpdateEnabled: true, // Set a default value
|
AutoUpdateEnabled: true, // Set a default value
|
||||||
ActiveExtension: "",
|
ActiveExtension: "",
|
||||||
|
KeyboardMacros: []KeyboardMacro{},
|
||||||
DisplayMaxBrightness: 64,
|
DisplayMaxBrightness: 64,
|
||||||
DisplayDimAfterSec: 120, // 2 minutes
|
DisplayDimAfterSec: 120, // 2 minutes
|
||||||
DisplayOffAfterSec: 1800, // 30 minutes
|
DisplayOffAfterSec: 1800, // 30 minutes
|
||||||
|
|
97
jsonrpc.go
97
jsonrpc.go
|
@ -792,6 +792,101 @@ func rpcSetScrollSensitivity(sensitivity string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getKeyboardMacros() (interface{}, error) {
|
||||||
|
macros := make([]KeyboardMacro, len(config.KeyboardMacros))
|
||||||
|
copy(macros, config.KeyboardMacros)
|
||||||
|
|
||||||
|
return macros, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyboardMacrosParams struct {
|
||||||
|
Macros []interface{} `json:"macros"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func setKeyboardMacros(params KeyboardMacrosParams) (interface{}, error) {
|
||||||
|
if params.Macros == nil {
|
||||||
|
return nil, fmt.Errorf("missing or invalid macros parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
newMacros := make([]KeyboardMacro, 0, len(params.Macros))
|
||||||
|
|
||||||
|
for i, item := range params.Macros {
|
||||||
|
macroMap, ok := item.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("invalid macro at index %d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
id, _ := macroMap["id"].(string)
|
||||||
|
if id == "" {
|
||||||
|
id = fmt.Sprintf("macro-%d", time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
name, _ := macroMap["name"].(string)
|
||||||
|
description, _ := macroMap["description"].(string)
|
||||||
|
|
||||||
|
sortOrder := i + 1
|
||||||
|
if sortOrderFloat, ok := macroMap["sortOrder"].(float64); ok {
|
||||||
|
sortOrder = int(sortOrderFloat)
|
||||||
|
}
|
||||||
|
|
||||||
|
steps := []KeyboardMacroStep{}
|
||||||
|
if stepsArray, ok := macroMap["steps"].([]interface{}); ok {
|
||||||
|
for _, stepItem := range stepsArray {
|
||||||
|
stepMap, ok := stepItem.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
step := KeyboardMacroStep{}
|
||||||
|
|
||||||
|
if keysArray, ok := stepMap["keys"].([]interface{}); ok {
|
||||||
|
for _, k := range keysArray {
|
||||||
|
if keyStr, ok := k.(string); ok {
|
||||||
|
step.Keys = append(step.Keys, keyStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if modsArray, ok := stepMap["modifiers"].([]interface{}); ok {
|
||||||
|
for _, m := range modsArray {
|
||||||
|
if modStr, ok := m.(string); ok {
|
||||||
|
step.Modifiers = append(step.Modifiers, modStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if delay, ok := stepMap["delay"].(float64); ok {
|
||||||
|
step.Delay = int(delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
steps = append(steps, step)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro := KeyboardMacro{
|
||||||
|
ID: id,
|
||||||
|
Name: name,
|
||||||
|
Description: description,
|
||||||
|
Steps: steps,
|
||||||
|
SortOrder: sortOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := macro.Validate(); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid macro at index %d: %w", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newMacros = append(newMacros, macro)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.KeyboardMacros = newMacros
|
||||||
|
|
||||||
|
if err := SaveConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
var rpcHandlers = map[string]RPCHandler{
|
var rpcHandlers = map[string]RPCHandler{
|
||||||
"ping": {Func: rpcPing},
|
"ping": {Func: rpcPing},
|
||||||
"getDeviceID": {Func: rpcGetDeviceID},
|
"getDeviceID": {Func: rpcGetDeviceID},
|
||||||
|
@ -857,4 +952,6 @@ var rpcHandlers = map[string]RPCHandler{
|
||||||
"setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}},
|
"setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}},
|
||||||
"getScrollSensitivity": {Func: rpcGetScrollSensitivity},
|
"getScrollSensitivity": {Func: rpcGetScrollSensitivity},
|
||||||
"setScrollSensitivity": {Func: rpcSetScrollSensitivity, Params: []string{"sensitivity"}},
|
"setScrollSensitivity": {Func: rpcSetScrollSensitivity, Params: []string{"sensitivity"}},
|
||||||
|
"getKeyboardMacros": {Func: getKeyboardMacros},
|
||||||
|
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue