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()
case "dc-power":
_ = unmountDCControl()
case "serial-buttons":
_ = unmountSerialButtons()
}
config.ActiveExtension = extensionId
if err := SaveConfig(); err != nil {
@ -768,6 +770,8 @@ func rpcSetActiveExtension(extensionId string) error {
_ = mountATXControl()
case "dc-power":
_ = mountDCControl()
case "serial-buttons":
_ = mountSerialButtons()
}
return nil
}
@ -802,6 +806,15 @@ func rpcGetATXState() (ATXState, error) {
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 {
BaudRate string `json:"baudRate"`
DataBits string `json:"dataBits"`
@ -1296,6 +1309,7 @@ var rpcHandlers = map[string]RPCHandler{
"setActiveExtension": {Func: rpcSetActiveExtension, Params: []string{"extensionId"}},
"getATXState": {Func: rpcGetATXState},
"setATXPowerAction": {Func: rpcSetATXPowerAction, Params: []string{"action"}},
"sendCustomCommand": {Func: rpcSendCustomCommand, Params: []string{"command"}},
"getSerialSettings": {Func: rpcGetSerialSettings},
"setSerialSettings": {Func: rpcSetSerialSettings, Params: []string{"settings"}},
"getSerialButtonConfig": {Func: rpcGetSerialButtonConfig},

View File

@ -251,6 +251,33 @@ func setDCRestoreState(state int) error {
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{
BaudRate: 115200,
DataBits: 8,

View File

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