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:
JackTheRooster 2025-03-26 23:51:45 -05:00
parent 34786647c0
commit c338533029
4 changed files with 56 additions and 44 deletions

View File

@ -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{

View File

@ -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))
} }

View File

@ -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);

View File

@ -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";