Add backend to send custom commands

This commit is contained in:
Severin Müller 2025-09-23 16:49:45 +02:00
parent b2b3ee40a7
commit d8f670fcba
3 changed files with 65 additions and 23 deletions

View File

@ -758,6 +758,8 @@ func rpcSetActiveExtension(extensionId string) error {
_ = unmountATXControl() _ = unmountATXControl()
case "dc-power": case "dc-power":
_ = unmountDCControl() _ = unmountDCControl()
case "serial-buttons":
_ = unmountSerialButtons()
} }
config.ActiveExtension = extensionId config.ActiveExtension = extensionId
if err := SaveConfig(); err != nil { if err := SaveConfig(); err != nil {
@ -768,6 +770,8 @@ func rpcSetActiveExtension(extensionId string) error {
_ = mountATXControl() _ = mountATXControl()
case "dc-power": case "dc-power":
_ = mountDCControl() _ = mountDCControl()
case "serial-buttons":
_ = mountSerialButtons()
} }
return nil return nil
} }
@ -802,6 +806,15 @@ func rpcGetATXState() (ATXState, error) {
return state, nil return state, nil
} }
func rpcSendCustomCommand(command string) error {
logger.Info().Str("Command", command).Msg("JSONRPC: Sending custom serial command")
err := sendCustomCommand(command)
if err != nil {
return fmt.Errorf("failed to set DC power state: %w", err)
}
return nil
}
type SerialSettings struct { type SerialSettings struct {
BaudRate string `json:"baudRate"` BaudRate string `json:"baudRate"`
DataBits string `json:"dataBits"` DataBits string `json:"dataBits"`
@ -1296,6 +1309,7 @@ var rpcHandlers = map[string]RPCHandler{
"setActiveExtension": {Func: rpcSetActiveExtension, Params: []string{"extensionId"}}, "setActiveExtension": {Func: rpcSetActiveExtension, Params: []string{"extensionId"}},
"getATXState": {Func: rpcGetATXState}, "getATXState": {Func: rpcGetATXState},
"setATXPowerAction": {Func: rpcSetATXPowerAction, Params: []string{"action"}}, "setATXPowerAction": {Func: rpcSetATXPowerAction, Params: []string{"action"}},
"sendCustomCommand": {Func: rpcSendCustomCommand, Params: []string{"command"}},
"getSerialSettings": {Func: rpcGetSerialSettings}, "getSerialSettings": {Func: rpcGetSerialSettings},
"setSerialSettings": {Func: rpcSetSerialSettings, Params: []string{"settings"}}, "setSerialSettings": {Func: rpcSetSerialSettings, Params: []string{"settings"}},
"getSerialButtonConfig": {Func: rpcGetSerialButtonConfig}, "getSerialButtonConfig": {Func: rpcGetSerialButtonConfig},

View File

@ -251,6 +251,33 @@ func setDCRestoreState(state int) error {
return nil return nil
} }
func mountSerialButtons() error {
_ = port.SetMode(defaultMode)
return nil
}
func unmountSerialButtons() error {
_ = reopenSerialPort()
return nil
}
func sendCustomCommand(command string) error {
scopedLogger := serialLogger.With().Str("service", "custom-buttons").Logger()
scopedLogger.Info().Str("Command", command).Msg("Sending custom command.")
_, err := port.Write([]byte("\n"))
if err != nil {
scopedLogger.Warn().Err(err).Msg("Failed to send serial output \\n")
return err
}
_, err = port.Write([]byte(command))
if err != nil {
scopedLogger.Warn().Err(err).Str("line", command).Msg("Failed to send serial output")
return err
}
return nil
}
var defaultMode = &serial.Mode{ var defaultMode = &serial.Mode{
BaudRate: 115200, BaudRate: 115200,
DataBits: 8, DataBits: 8,

View File

@ -76,15 +76,10 @@ export function SerialButtons() {
return; return;
} }
const cfg = resp.result as ButtonConfig;
console.log("loaded button config: ");
console.log(cfg);
setButtonConfig(resp.result as ButtonConfig); setButtonConfig(resp.result as ButtonConfig);
}); });
console.log("loaded loaded settings through effect."); });
}, [send]);
const handleSerialSettingChange = (setting: keyof SerialSettings, value: string) => { const handleSerialSettingChange = (setting: keyof SerialSettings, value: string) => {
const newSettings = { ...serialSettings, [setting]: value }; const newSettings = { ...serialSettings, [setting]: value };
@ -104,21 +99,27 @@ export function SerialButtons() {
notifications.error(`Failed to update button config: ${resp.error.data || "Unknown error"}`); notifications.error(`Failed to update button config: ${resp.error.data || "Unknown error"}`);
return; return;
} }
// setButtonConfig(newButtonConfig); setButtonConfig(newButtonConfig);
}); });
}; };
/** build final string to send: const onClickButton = (btn: QuickButton) => {
* if the user's button command already contains a terminator, we don't append the selected terminator safely
*/ /** build final string to send:
const buildOutgoing = (raw: string): string => { * if the user's button command already contains a terminator, we don't append the selected terminator safely
const t = buttonConfig.terminator ?? ""; */
return raw.endsWith("\r") || raw.endsWith("\n") ? raw : raw + t; const raw = btn.command;
}; const t = buttonConfig.terminator ?? "";
const command = raw.endsWith("\r") || raw.endsWith("\n") ? raw : raw + t;
send("sendCustomCommand", { command }, (resp: JsonRpcResponse) => {
if ("error" in resp) {
notifications.error(
`Failed to send ATX power action: ${resp.error.data || "Unknown error"}`,
);
}
});
const onClickButton = async (btn: QuickButton) => {
buildOutgoing(btn.command);
// Try to send via backend method
}; };
/** CRUD helpers */ /** CRUD helpers */
@ -134,11 +135,11 @@ export function SerialButtons() {
setDraftCmd(btn.command); setDraftCmd(btn.command);
}; };
// const removeBtn = async (id: string) => { const removeBtn = (id: string) => {
// const next = { ...buttonConfig, buttons: buttonConfig.buttons.filter(b => b.id !== id).map((b, i) => ({ ...b, sort: i })) }; const nextButtons = buttonConfig.buttons.filter(b => b.id !== id).map((b, i) => ({ ...b, sort: i })) ;
// // await setButtonConfig(next); handleSerialButtonConfigChange("buttons", stableSort(nextButtons) );
// setEditorOpen(null); setEditorOpen(null);
// }; };
const saveDraft = () => { const saveDraft = () => {
const label = draftLabel.trim() || "Unnamed"; const label = draftLabel.trim() || "Unnamed";
@ -327,7 +328,7 @@ export function SerialButtons() {
theme="danger" theme="danger"
LeadingIcon={LuTrash2} LeadingIcon={LuTrash2}
text="Delete" text="Delete"
// onClick={() => removeBtn(editorOpen!.id)} onClick={() => removeBtn(editorOpen.id!)}
aria-label={`Delete ${draftLabel}`} aria-label={`Delete ${draftLabel}`}
/>)} />)}
</div> </div>