mirror of https://github.com/jetkvm/kvm.git
now using gadget last input time to determine if jiggler can be activated
added UI elements to set jiggler config
This commit is contained in:
parent
34786647c0
commit
c338533029
|
@ -49,8 +49,8 @@ var defaultConfig = &Config{
|
||||||
DisplayOffAfterSec: 1800, // 30 minutes
|
DisplayOffAfterSec: 1800, // 30 minutes
|
||||||
JigglerConfig: &JigglerConfig{
|
JigglerConfig: &JigglerConfig{
|
||||||
InactivityLimitSeconds: 20,
|
InactivityLimitSeconds: 20,
|
||||||
JitterPercentage: 0.0,
|
JitterPercentage: 0,
|
||||||
ScheduleCronTab: "*/5 * * * * *",
|
ScheduleCronTab: "*/20 * * * * *",
|
||||||
},
|
},
|
||||||
TLSMode: "",
|
TLSMode: "",
|
||||||
UsbConfig: &usbgadget.Config{
|
UsbConfig: &usbgadget.Config{
|
||||||
|
|
26
jiggler.go
26
jiggler.go
|
@ -1,18 +1,18 @@
|
||||||
package kvm
|
package kvm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/go-co-op/gocron/v2"
|
"github.com/go-co-op/gocron/v2"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JigglerConfig struct {
|
type JigglerConfig struct {
|
||||||
InactivityLimitSeconds float64 `json:"inactivity_limit_seconds"`
|
InactivityLimitSeconds int `json:"inactivity_limit_seconds"`
|
||||||
JitterPercentage float64 `json:"jitter_percentage"`
|
JitterPercentage int `json:"jitter_percentage"`
|
||||||
ScheduleCronTab string `json:"schedule_cron_tab"`
|
ScheduleCronTab string `json:"schedule_cron_tab"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastUserInput = time.Now()
|
|
||||||
var jigglerEnabled = false
|
var jigglerEnabled = false
|
||||||
var jobDelta time.Duration = 0
|
var jobDelta time.Duration = 0
|
||||||
var scheduler gocron.Scheduler = nil
|
var scheduler gocron.Scheduler = nil
|
||||||
|
@ -28,18 +28,22 @@ func rpcGetJigglerConfig() (JigglerConfig, error) {
|
||||||
return *config.JigglerConfig, nil
|
return *config.JigglerConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcSetJigglerConfig(jigglerConfig JigglerConfig) {
|
func rpcSetJigglerConfig(jigglerConfig JigglerConfig) error {
|
||||||
|
logger.Infof("[jsonrpc.go:rpcSetJigglerConfig] jigglerConfig: %v, %v, %v", jigglerConfig.InactivityLimitSeconds, jigglerConfig.JitterPercentage, jigglerConfig.ScheduleCronTab)
|
||||||
config.JigglerConfig = &jigglerConfig
|
config.JigglerConfig = &jigglerConfig
|
||||||
err := removeExistingCrobJobs(scheduler)
|
err := removeExistingCrobJobs(scheduler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Error removing cron jobs from scheduler %v", err)
|
return fmt.Errorf("error removing cron jobs from scheduler %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
err = runJigglerCronTab()
|
err = runJigglerCronTab()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Error scheduling jiggler crontab: %v", err)
|
return fmt.Errorf("error scheduling jiggler crontab: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
err = SaveConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to save config: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeExistingCrobJobs(s gocron.Scheduler) error {
|
func removeExistingCrobJobs(s gocron.Scheduler) error {
|
||||||
|
@ -100,7 +104,7 @@ func runJiggler() {
|
||||||
time.Sleep(jitter)
|
time.Sleep(jitter)
|
||||||
}
|
}
|
||||||
inactivitySeconds := config.JigglerConfig.InactivityLimitSeconds
|
inactivitySeconds := config.JigglerConfig.InactivityLimitSeconds
|
||||||
if time.Since(lastUserInput) > time.Duration(inactivitySeconds)*time.Second {
|
if time.Since(gadget.GetLastUserInputTime()) > time.Duration(inactivitySeconds)*time.Second {
|
||||||
//TODO: change to rel mouse
|
//TODO: change to rel mouse
|
||||||
err := rpcAbsMouseReport(1, 1, 0)
|
err := rpcAbsMouseReport(1, 1, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -124,6 +128,6 @@ func calculateJobDelta(s gocron.Scheduler) (time.Duration, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateJitterDuration(delta time.Duration) time.Duration {
|
func calculateJitterDuration(delta time.Duration) time.Duration {
|
||||||
jitter := rand.Float64() * config.JigglerConfig.JitterPercentage * delta.Seconds()
|
jitter := rand.Float64() * float64(config.JigglerConfig.JitterPercentage) / 100 * delta.Seconds()
|
||||||
return time.Duration(jitter * float64(time.Second))
|
return time.Duration(jitter * float64(time.Second))
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,19 +37,19 @@ const jigglerCrontabConfigs = [
|
||||||
const jigglerJitterConfigs = [
|
const jigglerJitterConfigs = [
|
||||||
{
|
{
|
||||||
label: "No Jitter",
|
label: "No Jitter",
|
||||||
value: "0.0",
|
value: "0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "10%",
|
label: "10%",
|
||||||
value: ".1",
|
value: "20",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "25%",
|
label: "25%",
|
||||||
value: ".25",
|
value: "25",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "50%",
|
label: "50%",
|
||||||
value: ".5",
|
value: "50",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -75,13 +75,12 @@ const jigglerInactivityConfigs = [
|
||||||
export function JigglerSetting() {
|
export function JigglerSetting() {
|
||||||
const [send] = useJsonRpc();
|
const [send] = useJsonRpc();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [inactivityLimitSeconds, setInactivityLimitSeconds] = useState("");
|
|
||||||
const [jitterPercentage, setJitterPercentage] = useState("");
|
const [jitterPercentage, setJitterPercentage] = useState("");
|
||||||
const [scheduleCronTab, setScheduleCronTab] = useState("");
|
const [scheduleCronTab, setScheduleCronTab] = useState("");
|
||||||
|
|
||||||
const [jigglerConfigState, setJigglerConfigState] = useState<JigglerConfig>({
|
const [jigglerConfigState, setJigglerConfigState] = useState<JigglerConfig>({
|
||||||
inactivity_limit_seconds: 20.0,
|
inactivity_limit_seconds: 20,
|
||||||
jitter_percentage: 0.0,
|
jitter_percentage: 0,
|
||||||
schedule_cron_tab: "*/20 * * * * *"
|
schedule_cron_tab: "*/20 * * * * *"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -90,28 +89,32 @@ export function JigglerSetting() {
|
||||||
if ("error" in resp) return;
|
if ("error" in resp) return;
|
||||||
const result = resp.result as JigglerConfig;
|
const result = resp.result as JigglerConfig;
|
||||||
setJigglerConfigState(result);
|
setJigglerConfigState(result);
|
||||||
setInactivityLimitSeconds(String(result.inactivity_limit_seconds))
|
|
||||||
setJitterPercentage(String(result.jitter_percentage))
|
const jitterPercentage = jigglerJitterConfigs.map(u => u.value).includes(result.jitter_percentage.toString())
|
||||||
setScheduleCronTab(result.schedule_cron_tab)
|
? result.jitter_percentage.toString()
|
||||||
|
: "custom";
|
||||||
|
setJitterPercentage(jitterPercentage)
|
||||||
|
|
||||||
|
const scheduleCronTab = jigglerCrontabConfigs.map(u => u.value).includes(result.schedule_cron_tab)
|
||||||
|
? result.schedule_cron_tab
|
||||||
|
: "custom";
|
||||||
|
setScheduleCronTab(scheduleCronTab)
|
||||||
});
|
});
|
||||||
}, [send, setInactivityLimitSeconds]);
|
}, [send]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
syncJigglerConfig()
|
syncJigglerConfig()
|
||||||
}, [send, syncJigglerConfig]);
|
}, [send, syncJigglerConfig]);
|
||||||
|
|
||||||
const handleJigglerInactivityLimitSecondsChange = (value: string) => {
|
const handleJigglerInactivityLimitSecondsChange = (value: string) => {
|
||||||
setInactivityLimitSeconds(value)
|
|
||||||
setJigglerConfigState({ ...jigglerConfigState, inactivity_limit_seconds: Number(value) });
|
setJigglerConfigState({ ...jigglerConfigState, inactivity_limit_seconds: Number(value) });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleJigglerJitterPercentageChange = (value: string) => {
|
const handleJigglerJitterPercentageChange = (value: string) => {
|
||||||
setJitterPercentage(value)
|
|
||||||
setJigglerConfigState({ ...jigglerConfigState, jitter_percentage: Number(value) });
|
setJigglerConfigState({ ...jigglerConfigState, jitter_percentage: Number(value) });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleJigglerScheduleCronTabChange = (value: string) => {
|
const handleJigglerScheduleCronTabChange = (value: string) => {
|
||||||
setScheduleCronTab(value)
|
|
||||||
setJigglerConfigState({ ...jigglerConfigState, schedule_cron_tab: value });
|
setJigglerConfigState({ ...jigglerConfigState, schedule_cron_tab: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -121,7 +124,7 @@ export function JigglerSetting() {
|
||||||
send("setJigglerConfig", { jigglerConfig }, async resp => {
|
send("setJigglerConfig", { jigglerConfig }, async resp => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(
|
notifications.error(
|
||||||
`Failed to set jiggler config: ${resp.error.data || "Unknown error"}`,
|
`Failed to set jiggler config: ${resp.error.data || "Unknown error"}`,
|
||||||
);
|
);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
|
@ -146,23 +149,23 @@ export function JigglerSetting() {
|
||||||
value={scheduleCronTab}
|
value={scheduleCronTab}
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
if (e.target.value === "custom") {
|
setScheduleCronTab(e.target.value);
|
||||||
setScheduleCronTab(e.target.value);
|
if (e.target.value != "custom") {
|
||||||
} else {
|
handleJigglerScheduleCronTabChange(e.target.value);
|
||||||
handleJigglerScheduleCronTabChange(e.target.value)
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
options={[...jigglerCrontabConfigs, { value: "custom", label: "Custom" }]}
|
options={[...jigglerCrontabConfigs, {value: "custom", label: "Custom"}]}
|
||||||
/>
|
/>
|
||||||
{jitterPercentage === "custom" && (
|
{scheduleCronTab === "custom" && (
|
||||||
<InputFieldWithLabel
|
<InputFieldWithLabel
|
||||||
required
|
required
|
||||||
label="Jiggler Crontab"
|
label="Jiggler Crontab"
|
||||||
placeholder="Enter Cron Tab"
|
placeholder="*/20 * * * * *"
|
||||||
value={scheduleCronTab}
|
|
||||||
onChange={e => handleJigglerScheduleCronTabChange(e.target.value)}
|
onChange={e => handleJigglerScheduleCronTabChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
label="Jitter Percentage"
|
label="Jitter Percentage"
|
||||||
|
@ -170,27 +173,31 @@ export function JigglerSetting() {
|
||||||
value={jitterPercentage}
|
value={jitterPercentage}
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
if (e.target.value === "custom") {
|
setJitterPercentage(e.target.value);
|
||||||
setJitterPercentage(e.target.value);
|
if (e.target.value != "custom") {
|
||||||
} else {
|
|
||||||
handleJigglerJitterPercentageChange(e.target.value)
|
handleJigglerJitterPercentageChange(e.target.value)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
options={[...jigglerJitterConfigs, { value: "custom", label: "Custom" }]}
|
options={[...jigglerJitterConfigs, {value: "custom", label: "Custom"}]}
|
||||||
/>
|
/>
|
||||||
{jitterPercentage === "custom" && (
|
{jitterPercentage === "custom" && (
|
||||||
<InputFieldWithLabel
|
<InputFieldWithLabel
|
||||||
required
|
required
|
||||||
label="Jitter Percentage"
|
label="Jitter Percentage"
|
||||||
placeholder="0.0"
|
placeholder="30"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="100"
|
||||||
onChange={e => handleJigglerJitterPercentageChange(e.target.value)}
|
onChange={e => handleJigglerJitterPercentageChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
label="Inactivity Limit Seconds"
|
label="Inactivity Limit Seconds"
|
||||||
className="max-w-[192px]"
|
className="max-w-[192px]"
|
||||||
value={inactivityLimitSeconds}
|
value={jigglerConfigState.inactivity_limit_seconds}
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
handleJigglerInactivityLimitSecondsChange(e.target.value);
|
handleJigglerInactivityLimitSecondsChange(e.target.value);
|
||||||
|
|
|
@ -9,14 +9,15 @@ import { useDeviceSettingsStore, useSettingsStore } from "@/hooks/stores";
|
||||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
import notifications from "@/notifications";
|
import notifications from "@/notifications";
|
||||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||||
|
import { JigglerSetting } from "@components/JigglerSetting";
|
||||||
|
import { SettingsSectionHeader } from "@components/SettingsSectionHeader";
|
||||||
|
|
||||||
import { FeatureFlag } from "../components/FeatureFlag";
|
import { FeatureFlag } from "../components/FeatureFlag";
|
||||||
import { SelectMenuBasic } from "../components/SelectMenuBasic";
|
import { SelectMenuBasic } from "../components/SelectMenuBasic";
|
||||||
import { useFeatureFlag } from "../hooks/useFeatureFlag";
|
import { useFeatureFlag } from "../hooks/useFeatureFlag";
|
||||||
|
|
||||||
import { SettingsItem } from "./devices.$id.settings";
|
import { SettingsItem } from "./devices.$id.settings";
|
||||||
import { JigglerSetting } from "@components/JigglerSetting";
|
|
||||||
import { SettingsSectionHeader } from "@components/SettingsSectionHeader";
|
|
||||||
|
|
||||||
type ScrollSensitivity = "low" | "default" | "high";
|
type ScrollSensitivity = "low" | "default" | "high";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue