Adding HDMI Enable/Disable Toggle for PCIe

This commit is contained in:
Thomas Pressnell
2025-05-07 22:05:41 +01:00
parent f242b5a378
commit 6935923bd1
7 changed files with 166 additions and 2 deletions

View File

@@ -35,7 +35,10 @@ func vmRouter(r *gin.Engine) {
api.GET("/vm/oled", service.GetOLED) // get OLED configuration
api.POST("/vm/oled", service.SetOLED) // set OLED configuration
api.POST("/vm/hdmi/reset", service.ResetHdmi) // reset hdmi (pcie only)
api.GET("/vm/hdmi", service.GetHdmiState) // get HDMI state
api.POST("/vm/hdmi/reset", service.ResetHdmi) // reset hdmi (pcie only)
api.POST("/vm/hdmi/enable", service.EnableHdmi) // enable hdmi
api.POST("/vm/hdmi/disable", service.DisableHdmi) // disable hdmi
api.GET("/vm/ssh", service.GetSSHState) // get SSH state
api.POST("/vm/ssh/enable", service.EnableSSH) // enable SSH

View File

@@ -9,6 +9,9 @@ import (
log "github.com/sirupsen/logrus"
)
// ideally read state from the device
var hdmiEnabled = true
func (s *Service) ResetHdmi(c *gin.Context) {
var rsp proto.Response
@@ -17,7 +20,48 @@ func (s *Service) ResetHdmi(c *gin.Context) {
vision.SetHDMI(false)
time.Sleep(1 * time.Second)
vision.SetHDMI(true)
hdmiEnabled = true
rsp.OkRsp(c)
log.Debug("reset hdmi")
}
func (s *Service) EnableHdmi(c *gin.Context) {
var rsp proto.Response
vision := common.GetKvmVision()
vision.SetHDMI(true)
hdmiEnabled = true
rsp.OkRsp(c)
log.Debug("enable hdmi")
}
func (s *Service) DisableHdmi(c *gin.Context) {
var rsp proto.Response
vision := common.GetKvmVision()
vision.SetHDMI(false)
hdmiEnabled = false
rsp.OkRsp(c)
log.Debug("disable hdmi")
}
func (s *Service) GetHdmiState(c *gin.Context) {
var rsp proto.Response
if hdmiEnabled {
rsp.OkRspWithData(c, &map[string]string{
"state": "enabled",
})
} else {
rsp.OkRspWithData(c, &map[string]string{
"state": "disabled",
})
}
log.Debug("get hdmi state")
}

View File

@@ -62,6 +62,29 @@ export function resetHdmi() {
return http.post('/api/vm/hdmi/reset');
}
// get HDMI state
export function getHdmiState() {
return http.get('/api/vm/hdmi');
}
// enable HDMI
export function enableHdmi() {
return http.post('/api/vm/hdmi/enable');
}
// disable HDMI
export function disableHdmi() {
return http.post('/api/vm/hdmi/disable');
}
// set HDMI state
export function setHdmiState(state: string) {
if (state === 'enabled') {
return enableHdmi();
}
return disableHdmi();
}
// get SSH state
export function getSSHState() {
return http.get('/api/vm/ssh');

View File

@@ -64,7 +64,9 @@ const en = {
frameDetect: 'Frame Detect',
frameDetectTip:
"Calculate the difference between frames. Stop transmitting video stream when no changes are detected on the remote host's screen.",
resetHdmi: 'Reset HDMI'
resetHdmi: 'Reset HDMI',
enableHdmi: 'Enable HDMI',
disableHdmi: 'Disable HDMI',
},
keyboard: {
title: 'Keyboard',

4
web/src/jotai/hdmi.ts Normal file
View File

@@ -0,0 +1,4 @@
import { atom } from 'jotai';
// hid mode: normal or hid-only
export const hdmiEnabledAtom = atom<'enabled' | 'disabled'>('enabled');

View File

@@ -0,0 +1,86 @@
import { useEffect, useState } from 'react';
import clsx from 'clsx';
import { useAtom } from 'jotai';
import { PenIcon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import * as api from '@/api/vm.ts';
import { hdmiEnabledAtom } from '@/jotai/hdmi.ts';
export const HdmiState = () => {
const { t } = useTranslation();
const [hdmiMode, setHdmiMode] = useAtom(hdmiEnabledAtom);
const [isPcie, setIsPcie] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const [_, setErrMsg] = useState('');
useEffect(() => {
getHdmiState();
api.getHardware().then((rsp) => {
if (rsp.code === 0) {
setIsPcie(rsp.data?.version === 'PCIE');
}
});
}, []);
function getHdmiState() {
setIsLoading(true);
api
.getHdmiState()
.then((rsp) => {
if (rsp.code !== 0) {
setErrMsg(rsp.msg);
return;
}
setHdmiMode(rsp.data.state);
})
.finally(() => {
setIsLoading(false);
});
}
function updateHDMIState() {
if (isLoading) return;
setIsLoading(true);
const mode = hdmiMode === 'enabled' ? 'disabled' : 'enabled';
api
.setHdmiState(mode)
.then((rsp) => {
setIsLoading(false);
if (rsp.code !== 0) {
console.log(rsp.msg);
} else {
setHdmiMode(mode);
}
})
.catch((err) => {
console.log(err);
setIsLoading(false);
});
}
return (
<>
{isPcie ? (
<div
className={clsx(
'flex h-[30px] cursor-pointer select-none items-center space-x-2 rounded px-3 hover:bg-neutral-700/70',
'text-neutral-300'
)}
onClick={updateHDMIState}
>
<PenIcon size={18} />
<span>{hdmiMode === 'enabled' ? t('screen.disableHdmi') : t('screen.enableHdmi')}</span>
</div>
) : (
<></>
)}
</>
);
};

View File

@@ -16,6 +16,7 @@ import { Quality } from './quality';
import { Reset } from './reset.tsx';
import { Resolution } from './resolution';
import { VideoMode } from './video-mode.tsx';
import { HdmiState } from './hdmi-state.tsx';
export const Screen = () => {
const { t } = useTranslation();
@@ -76,6 +77,7 @@ export const Screen = () => {
<Fps fps={fps} setFps={setFps} />
{videoMode === 'mjpeg' ? <FrameDetect /> : <Gop gop={gop} setGop={setGop} />}
<Reset />
<HdmiState />
</div>
);