From a982ec571f46a7182eb787ac3ec283f88b75bd4f Mon Sep 17 00:00:00 2001 From: JackTheRooster Date: Sun, 23 Mar 2025 23:56:30 -0500 Subject: [PATCH 01/10] added JigglerConfig struct added jiggler scheduler jitter randomizes jiggler activation --- config.go | 9 +- go.mod | 3 + go.sum | 6 + jiggler.go | 118 ++++++++++++++++--- jsonrpc.go | 2 + ui/src/routes/devices.$id.settings.mouse.tsx | 48 ++++++++ 6 files changed, 170 insertions(+), 16 deletions(-) diff --git a/config.go b/config.go index 642f113..a6d878e 100644 --- a/config.go +++ b/config.go @@ -20,6 +20,7 @@ type Config struct { CloudToken string `json:"cloud_token"` GoogleIdentity string `json:"google_identity"` JigglerEnabled bool `json:"jiggler_enabled"` + JigglerConfig *JigglerConfig `json:"jiggler_config"` AutoUpdateEnabled bool `json:"auto_update_enabled"` IncludePreRelease bool `json:"include_pre_release"` HashedPassword string `json:"hashed_password"` @@ -46,7 +47,13 @@ var defaultConfig = &Config{ DisplayMaxBrightness: 64, DisplayDimAfterSec: 120, // 2 minutes DisplayOffAfterSec: 1800, // 30 minutes - TLSMode: "", + JigglerConfig: &JigglerConfig{ + ActiveAfterSeconds: 20, + JitterEnabled: false, + JitterPercentage: .25, + ScheduleCronTab: "*/5 * * * * *", + }, + TLSMode: "", UsbConfig: &usbgadget.Config{ VendorId: "0x1d6b", //The Linux Foundation ProductId: "0x0104", //Multifunction Composite Gadget diff --git a/go.mod b/go.mod index 93fedab..a2bbed5 100644 --- a/go.mod +++ b/go.mod @@ -40,11 +40,13 @@ require ( github.com/creack/goselect v0.1.2 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-co-op/gocron/v2 v2.16.1 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect @@ -70,6 +72,7 @@ require ( github.com/pion/turn/v4 v4.0.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/vishvananda/netns v0.0.4 // indirect diff --git a/go.sum b/go.sum index b5769d8..134074b 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-co-op/gocron/v2 v2.16.1 h1:ux/5zxVRveCaCuTtNI3DiOk581KC1KpJbpJFYUEVYwo= +github.com/go-co-op/gocron/v2 v2.16.1/go.mod h1:opexeOFy5BplhsKdA7bzY9zeYih8I8/WNJ4arTIFPVc= github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -56,6 +58,8 @@ github.com/hanwen/go-fuse/v2 v2.5.1 h1:OQBE8zVemSocRxA4OaFJbjJ5hlpCmIWbGr7r0M4uo github.com/hanwen/go-fuse/v2 v2.5.1/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs= github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY= github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= @@ -135,6 +139,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE= github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/jiggler.go b/jiggler.go index daec192..f35f2df 100644 --- a/jiggler.go +++ b/jiggler.go @@ -1,12 +1,22 @@ package kvm import ( + "github.com/go-co-op/gocron/v2" + "math/rand" "time" ) -var lastUserInput = time.Now() +type JigglerConfig struct { + ActiveAfterSeconds int `json:"active_after_seconds"` + JitterEnabled bool `json:"jitter_enabled"` + JitterPercentage float64 `json:"jitter_percentage"` + ScheduleCronTab string `json:"schedule_cron_tab"` +} +var lastUserInput = time.Now() var jigglerEnabled = false +var jobDelta time.Duration = 0 +var scheduler gocron.Scheduler = nil func rpcSetJigglerState(enabled bool) { jigglerEnabled = enabled @@ -15,27 +25,105 @@ func rpcGetJigglerState() bool { return jigglerEnabled } +func rpcGetJigglerConfig() (JigglerConfig, error) { + return *config.JigglerConfig, nil +} + +func rpcSetJigglerConfig(jigglerConfig JigglerConfig) { + config.JigglerConfig = &jigglerConfig + err := removeExistingCrobJobs(scheduler) + if err != nil { + logger.Errorf("Error removing cron jobs from scheduler %v", err) + return + } + err = runJigglerCronTab() + if err != nil { + logger.Errorf("Error scheduling jiggler crontab: %v", err) + return + } +} + +func removeExistingCrobJobs(s gocron.Scheduler) error { + for _, j := range s.Jobs() { + err := s.RemoveJob(j.ID()) + if err != nil { + return err + } + } + return nil +} + func init() { ensureConfigLoaded() + err := runJigglerCronTab() + if err != nil { + logger.Errorf("Error scheduling jiggler crontab: %v", err) + return + } +} - go runJiggler() +func runJigglerCronTab() error { + cronTab := config.JigglerConfig.ScheduleCronTab + s, err := gocron.NewScheduler() + if err != nil { + return err + } + scheduler = s + _, err = s.NewJob( + gocron.CronJob( + cronTab, + true, + ), + gocron.NewTask( + func() { + runJiggler() + }, + ), + ) + if err != nil { + return err + } + s.Start() + delta, err := calculateJobDelta(s) + jobDelta = delta + if err != nil { + return err + } + return nil } func runJiggler() { - for { - if jigglerEnabled { - if time.Since(lastUserInput) > 20*time.Second { - //TODO: change to rel mouse - err := rpcAbsMouseReport(1, 1, 0) - if err != nil { - logger.Warnf("Failed to jiggle mouse: %v", err) - } - err = rpcAbsMouseReport(0, 0, 0) - if err != nil { - logger.Warnf("Failed to reset mouse position: %v", err) - } + if jigglerEnabled { + if config.JigglerConfig.JitterEnabled { + jitter := calculateJitterDuration(jobDelta) + logger.Infof("Jitter enabled, Sleeping for %v", jitter) + time.Sleep(jitter) + } + activeAfterSeconds := config.JigglerConfig.ActiveAfterSeconds + if time.Since(lastUserInput) > time.Duration(activeAfterSeconds)*time.Second { + //TODO: change to rel mouse + err := rpcAbsMouseReport(1, 1, 0) + if err != nil { + logger.Warnf("Failed to jiggle mouse: %v", err) + } + err = rpcAbsMouseReport(0, 0, 0) + if err != nil { + logger.Warnf("Failed to reset mouse position: %v", err) } } - time.Sleep(20 * time.Second) } } + +func calculateJobDelta(s gocron.Scheduler) (time.Duration, error) { + j := s.Jobs()[0] + runs, err := j.NextRuns(2) + if err != nil { + return 0.0, err + } + return runs[1].Sub(runs[0]), nil +} + +func calculateJitterDuration(delta time.Duration) time.Duration { + jitter := rand.Float64() * config.JigglerConfig.JitterPercentage * delta.Seconds() + return time.Duration(jitter * float64(time.Second)) +} diff --git a/jsonrpc.go b/jsonrpc.go index 64935e1..f5dd3b4 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -807,6 +807,8 @@ var rpcHandlers = map[string]RPCHandler{ "rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}}, "setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}}, "getJigglerState": {Func: rpcGetJigglerState}, + "setJigglerConfig": {Func: rpcSetJigglerConfig, Params: []string{"setJigglerConfig"}}, + "getJigglerConfig": {Func: rpcGetJigglerConfig}, "sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}}, "getStreamQualityFactor": {Func: rpcGetStreamQualityFactor}, "setStreamQualityFactor": {Func: rpcSetStreamQualityFactor, Params: []string{"factor"}}, diff --git a/ui/src/routes/devices.$id.settings.mouse.tsx b/ui/src/routes/devices.$id.settings.mouse.tsx index 1d3a6cd..41f78d2 100644 --- a/ui/src/routes/devices.$id.settings.mouse.tsx +++ b/ui/src/routes/devices.$id.settings.mouse.tsx @@ -12,9 +12,17 @@ import { FeatureFlag } from "../components/FeatureFlag"; import { SelectMenuBasic } from "../components/SelectMenuBasic"; import { useFeatureFlag } from "../hooks/useFeatureFlag"; import { SettingsItem } from "./devices.$id.settings"; +import {InputFieldWithLabel} from "@components/InputField"; type ScrollSensitivity = "low" | "default" | "high"; +export interface JigglerConfig { + active_after_seconds: number; + jitter_enabled: boolean; + jitter_percentage: number; + schedule_cron_tab: string; +} + export default function SettingsKeyboardMouseRoute() { const hideCursor = useSettingsStore(state => state.isCursorHidden); const setHideCursor = useSettingsStore(state => state.setCursorVisibility); @@ -30,6 +38,12 @@ export default function SettingsKeyboardMouseRoute() { const { isEnabled: isScrollSensitivityEnabled } = useFeatureFlag("0.3.8"); const [jiggler, setJiggler] = useState(false); + const [jigglerConfig, setJigglerConfig] = useState({ + active_after_seconds: 0, + jitter_enabled: false, + jitter_percentage: 0.0, + schedule_cron_tab: "*/20 * * * * *" + }); const [send] = useJsonRpc(); @@ -45,6 +59,11 @@ export default function SettingsKeyboardMouseRoute() { setScrollSensitivity(resp.result as ScrollSensitivity); }); } + + send("getJigglerConfig", {}, resp => { + if ("error" in resp) return; + setJiggler(resp.result as boolean); + }); }, [isScrollSensitivityEnabled, send, setScrollSensitivity]); const handleJigglerChange = (enabled: boolean) => { @@ -59,6 +78,23 @@ export default function SettingsKeyboardMouseRoute() { }); }; + // const handleJigglerActiveAfterSecondsChange = (value: number) => { + // setJigglerConfig({ ...jigglerConfig, active_after_seconds: value }); + // }; + // + // const handleJigglerJitterEnabledChange = (value: boolean) => { + // setJigglerConfig({ ...jigglerConfig, jitter_enabled: value }); + // }; + // + // const handleJigglerJitterPercentageChange = (value: number) => { + // setJigglerConfig({ ...jigglerConfig, jitter_percentage: value }); + // }; + + const handleJigglerScheduleCronTabChange = (value: string) => { + setJigglerConfig({ ...jigglerConfig, schedule_cron_tab: value }); + }; + + const onScrollSensitivityChange = useCallback( (e: React.ChangeEvent) => { const sensitivity = e.target.value as ScrollSensitivity; @@ -124,6 +160,18 @@ export default function SettingsKeyboardMouseRoute() { onChange={e => handleJigglerChange(e.target.checked)} /> + + handleJigglerScheduleCronTabChange(e.target.value)} + /> +
From 9ecbe833ae17f0e97f2832a43d64631047ffeda6 Mon Sep 17 00:00:00 2001 From: JackTheRooster Date: Mon, 24 Mar 2025 22:08:42 -0500 Subject: [PATCH 02/10] moved jiggler settings to separate component --- ui/src/components/JigglerSetting.tsx | 93 ++++++++++++++++++++ ui/src/routes/devices.$id.settings.mouse.tsx | 44 +-------- 2 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 ui/src/components/JigglerSetting.tsx diff --git a/ui/src/components/JigglerSetting.tsx b/ui/src/components/JigglerSetting.tsx new file mode 100644 index 0000000..001c364 --- /dev/null +++ b/ui/src/components/JigglerSetting.tsx @@ -0,0 +1,93 @@ +import { useCallback } from "react"; +import { Button } from "@components/Button"; +import { InputFieldWithLabel } from "./InputField"; + +import { useEffect, useState } from "react"; +import { useJsonRpc } from "../hooks/useJsonRpc"; +import notifications from "../notifications"; + +export interface JigglerConfig { + active_after_seconds: number; + jitter_enabled: boolean; + jitter_percentage: number; + schedule_cron_tab: string; +} + +export function JigglerSetting() { + const [send] = useJsonRpc(); + const [loading, setLoading] = useState(false); + + const [jigglerConfigState, setJigglerConfigState] = useState({ + active_after_seconds: 0, + jitter_enabled: false, + jitter_percentage: 0.0, + schedule_cron_tab: "*/20 * * * * *" + }); + + useEffect(() => { + send("getJigglerConfig", {}, resp => { + if ("error" in resp) return; + setJigglerConfigState(resp.result as JigglerConfig); + }); + }, [send]); + + // const handleJigglerActiveAfterSecondsChange = (value: number) => { + // setJigglerConfig({ ...jigglerConfig, active_after_seconds: value }); + // }; + // + // const handleJigglerJitterEnabledChange = (value: boolean) => { + // setJigglerConfig({ ...jigglerConfig, jitter_enabled: value }); + // }; + // + // const handleJigglerJitterPercentageChange = (value: number) => { + // setJigglerConfig({ ...jigglerConfig, jitter_percentage: value }); + // }; + + const handleJigglerScheduleCronTabChange = (value: string) => { + setJigglerConfigState({ ...jigglerConfigState, schedule_cron_tab: value }); + }; + + const handleJigglerConfigChange = useCallback( + (jigglerConfig: JigglerConfig) => { + setLoading(true); + send("setJigglerConfig", { jigglerConfig }, async resp => { + if ("error" in resp) { + notifications.error( + `Failed to set jiggler config: ${resp.error.data || "Unknown error"}`, + ); + setLoading(false); + return; + } + + setLoading(false); + notifications.success( + `Jiggler Config successfully updated`, + ); + }); + }, + [send], + ); + + return ( +
+
+ handleJigglerScheduleCronTabChange(e.target.value)} + /> +
+
+
+
+ ); +} diff --git a/ui/src/routes/devices.$id.settings.mouse.tsx b/ui/src/routes/devices.$id.settings.mouse.tsx index 41f78d2..e749b4a 100644 --- a/ui/src/routes/devices.$id.settings.mouse.tsx +++ b/ui/src/routes/devices.$id.settings.mouse.tsx @@ -12,17 +12,10 @@ import { FeatureFlag } from "../components/FeatureFlag"; import { SelectMenuBasic } from "../components/SelectMenuBasic"; import { useFeatureFlag } from "../hooks/useFeatureFlag"; import { SettingsItem } from "./devices.$id.settings"; -import {InputFieldWithLabel} from "@components/InputField"; +import {JigglerSetting} from "@components/JigglerSetting"; type ScrollSensitivity = "low" | "default" | "high"; -export interface JigglerConfig { - active_after_seconds: number; - jitter_enabled: boolean; - jitter_percentage: number; - schedule_cron_tab: string; -} - export default function SettingsKeyboardMouseRoute() { const hideCursor = useSettingsStore(state => state.isCursorHidden); const setHideCursor = useSettingsStore(state => state.setCursorVisibility); @@ -38,12 +31,6 @@ export default function SettingsKeyboardMouseRoute() { const { isEnabled: isScrollSensitivityEnabled } = useFeatureFlag("0.3.8"); const [jiggler, setJiggler] = useState(false); - const [jigglerConfig, setJigglerConfig] = useState({ - active_after_seconds: 0, - jitter_enabled: false, - jitter_percentage: 0.0, - schedule_cron_tab: "*/20 * * * * *" - }); const [send] = useJsonRpc(); @@ -78,23 +65,6 @@ export default function SettingsKeyboardMouseRoute() { }); }; - // const handleJigglerActiveAfterSecondsChange = (value: number) => { - // setJigglerConfig({ ...jigglerConfig, active_after_seconds: value }); - // }; - // - // const handleJigglerJitterEnabledChange = (value: boolean) => { - // setJigglerConfig({ ...jigglerConfig, jitter_enabled: value }); - // }; - // - // const handleJigglerJitterPercentageChange = (value: number) => { - // setJigglerConfig({ ...jigglerConfig, jitter_percentage: value }); - // }; - - const handleJigglerScheduleCronTabChange = (value: string) => { - setJigglerConfig({ ...jigglerConfig, schedule_cron_tab: value }); - }; - - const onScrollSensitivityChange = useCallback( (e: React.ChangeEvent) => { const sensitivity = e.target.value as ScrollSensitivity; @@ -161,16 +131,10 @@ export default function SettingsKeyboardMouseRoute() { /> - handleJigglerScheduleCronTabChange(e.target.value)} - /> +
From 7f29d5a08f89af4b9387b4c88e84c28ef101fd4e Mon Sep 17 00:00:00 2001 From: JackTheRooster Date: Mon, 24 Mar 2025 23:47:37 -0500 Subject: [PATCH 03/10] jiggler settings now persist --- config.go | 7 +- jiggler.go | 13 ++-- jsonrpc.go | 2 +- ui/src/components/JigglerSetting.tsx | 71 +++++++++++--------- ui/src/routes/devices.$id.settings.mouse.tsx | 19 ++++-- 5 files changed, 60 insertions(+), 52 deletions(-) diff --git a/config.go b/config.go index a6d878e..cb1d35e 100644 --- a/config.go +++ b/config.go @@ -48,10 +48,9 @@ var defaultConfig = &Config{ DisplayDimAfterSec: 120, // 2 minutes DisplayOffAfterSec: 1800, // 30 minutes JigglerConfig: &JigglerConfig{ - ActiveAfterSeconds: 20, - JitterEnabled: false, - JitterPercentage: .25, - ScheduleCronTab: "*/5 * * * * *", + InactivityLimitSeconds: 20, + JitterPercentage: 0.0, + ScheduleCronTab: "*/5 * * * * *", }, TLSMode: "", UsbConfig: &usbgadget.Config{ diff --git a/jiggler.go b/jiggler.go index f35f2df..7a40271 100644 --- a/jiggler.go +++ b/jiggler.go @@ -7,10 +7,9 @@ import ( ) type JigglerConfig struct { - ActiveAfterSeconds int `json:"active_after_seconds"` - JitterEnabled bool `json:"jitter_enabled"` - JitterPercentage float64 `json:"jitter_percentage"` - ScheduleCronTab string `json:"schedule_cron_tab"` + InactivityLimitSeconds float64 `json:"inactivity_limit_seconds"` + JitterPercentage float64 `json:"jitter_percentage"` + ScheduleCronTab string `json:"schedule_cron_tab"` } var lastUserInput = time.Now() @@ -94,13 +93,13 @@ func runJigglerCronTab() error { func runJiggler() { if jigglerEnabled { - if config.JigglerConfig.JitterEnabled { + if config.JigglerConfig.JitterPercentage != 0 { jitter := calculateJitterDuration(jobDelta) logger.Infof("Jitter enabled, Sleeping for %v", jitter) time.Sleep(jitter) } - activeAfterSeconds := config.JigglerConfig.ActiveAfterSeconds - if time.Since(lastUserInput) > time.Duration(activeAfterSeconds)*time.Second { + inactivitySeconds := config.JigglerConfig.InactivityLimitSeconds + if time.Since(lastUserInput) > time.Duration(inactivitySeconds)*time.Second { //TODO: change to rel mouse err := rpcAbsMouseReport(1, 1, 0) if err != nil { diff --git a/jsonrpc.go b/jsonrpc.go index f5dd3b4..c9663ec 100644 --- a/jsonrpc.go +++ b/jsonrpc.go @@ -807,7 +807,7 @@ var rpcHandlers = map[string]RPCHandler{ "rpcMountBuiltInImage": {Func: rpcMountBuiltInImage, Params: []string{"filename"}}, "setJigglerState": {Func: rpcSetJigglerState, Params: []string{"enabled"}}, "getJigglerState": {Func: rpcGetJigglerState}, - "setJigglerConfig": {Func: rpcSetJigglerConfig, Params: []string{"setJigglerConfig"}}, + "setJigglerConfig": {Func: rpcSetJigglerConfig, Params: []string{"jigglerConfig"}}, "getJigglerConfig": {Func: rpcGetJigglerConfig}, "sendWOLMagicPacket": {Func: rpcSendWOLMagicPacket, Params: []string{"macAddress"}}, "getStreamQualityFactor": {Func: rpcGetStreamQualityFactor}, diff --git a/ui/src/components/JigglerSetting.tsx b/ui/src/components/JigglerSetting.tsx index 001c364..5a4c3e3 100644 --- a/ui/src/components/JigglerSetting.tsx +++ b/ui/src/components/JigglerSetting.tsx @@ -7,8 +7,7 @@ import { useJsonRpc } from "../hooks/useJsonRpc"; import notifications from "../notifications"; export interface JigglerConfig { - active_after_seconds: number; - jitter_enabled: boolean; + inactivity_limit_seconds: number; jitter_percentage: number; schedule_cron_tab: string; } @@ -18,26 +17,25 @@ export function JigglerSetting() { const [loading, setLoading] = useState(false); const [jigglerConfigState, setJigglerConfigState] = useState({ - active_after_seconds: 0, - jitter_enabled: false, - jitter_percentage: 0.0, - schedule_cron_tab: "*/20 * * * * *" + inactivity_limit_seconds: 20.0, + jitter_percentage: 0.0, + schedule_cron_tab: "*/20 * * * * *" }); - useEffect(() => { + const syncJigglerConfig = useCallback(() => { send("getJigglerConfig", {}, resp => { if ("error" in resp) return; setJigglerConfigState(resp.result as JigglerConfig); }); }, [send]); - // const handleJigglerActiveAfterSecondsChange = (value: number) => { - // setJigglerConfig({ ...jigglerConfig, active_after_seconds: value }); - // }; - // - // const handleJigglerJitterEnabledChange = (value: boolean) => { - // setJigglerConfig({ ...jigglerConfig, jitter_enabled: value }); - // }; + useEffect(() => { + syncJigglerConfig() + }, [send, syncJigglerConfig]); + + const handleJigglerInactivityLimitSecondsChange = (value: string) => { + setJigglerConfigState({ ...jigglerConfigState, inactivity_limit_seconds: Number(value) }); + }; // // const handleJigglerJitterPercentageChange = (value: number) => { // setJigglerConfig({ ...jigglerConfig, jitter_percentage: value }); @@ -48,24 +46,24 @@ export function JigglerSetting() { }; const handleJigglerConfigChange = useCallback( - (jigglerConfig: JigglerConfig) => { - setLoading(true); - send("setJigglerConfig", { jigglerConfig }, async resp => { - if ("error" in resp) { - notifications.error( - `Failed to set jiggler config: ${resp.error.data || "Unknown error"}`, - ); - setLoading(false); - return; - } - - setLoading(false); - notifications.success( - `Jiggler Config successfully updated`, + (jigglerConfig: JigglerConfig) => { + setLoading(true); + send("setJigglerConfig", { jigglerConfig }, async resp => { + if ("error" in resp) { + notifications.error( + `Failed to set jiggler config: ${resp.error.data || "Unknown error"}`, ); - }); - }, - [send], + setLoading(false); + return; + } + setLoading(false); + notifications.success( + `Jiggler Config successfully updated`, + ); + syncJigglerConfig(); + }); + }, + [send, syncJigglerConfig], ); return ( @@ -74,10 +72,17 @@ export function JigglerSetting() { handleJigglerScheduleCronTabChange(e.target.value)} /> + handleJigglerInactivityLimitSecondsChange(e.target.value)} + />