mirror of https://github.com/jetkvm/kvm.git
Compare commits
6 Commits
79bac39b6b
...
a6eab94e0d
Author | SHA1 | Date |
---|---|---|
|
a6eab94e0d | |
|
309d30d3c3 | |
|
cabe5b07ab | |
|
7d1777985f | |
|
e9f140c735 | |
|
34e42fd37c |
|
@ -23,8 +23,8 @@ type Config struct {
|
||||||
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
|
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
|
||||||
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
|
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
|
||||||
DisplayMaxBrightness int `json:"display_max_brightness"`
|
DisplayMaxBrightness int `json:"display_max_brightness"`
|
||||||
DisplayDimAfterMs int64 `json:"display_dim_after_ms"`
|
DisplayDimAfterSec int `json:"display_dim_after_sec"`
|
||||||
DisplayOffAfterMs int64 `json:"display_off_after_ms"`
|
DisplayOffAfterSec int `json:"display_off_after_sec"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const configPath = "/userdata/kvm_config.json"
|
const configPath = "/userdata/kvm_config.json"
|
||||||
|
@ -33,8 +33,8 @@ var defaultConfig = &Config{
|
||||||
CloudURL: "https://api.jetkvm.com",
|
CloudURL: "https://api.jetkvm.com",
|
||||||
AutoUpdateEnabled: true, // Set a default value
|
AutoUpdateEnabled: true, // Set a default value
|
||||||
DisplayMaxBrightness: 64,
|
DisplayMaxBrightness: 64,
|
||||||
DisplayDimAfterMs: 120000, // 2 minutes
|
DisplayDimAfterSec: 120, // 2 minutes
|
||||||
DisplayOffAfterMs: 1800000, // 30 minutes
|
DisplayOffAfterSec: 1800, // 30 minutes
|
||||||
}
|
}
|
||||||
|
|
||||||
var config *Config
|
var config *Config
|
||||||
|
|
83
display.go
83
display.go
|
@ -77,7 +77,7 @@ func requestDisplayUpdate() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
wakeDisplay()
|
wakeDisplay(false)
|
||||||
fmt.Println("display updating........................")
|
fmt.Println("display updating........................")
|
||||||
//TODO: only run once regardless how many pending updates
|
//TODO: only run once regardless how many pending updates
|
||||||
updateDisplay()
|
updateDisplay()
|
||||||
|
@ -149,22 +149,28 @@ func tick_displayOff() {
|
||||||
|
|
||||||
// wakeDisplay sets the display brightness back to config.DisplayMaxBrightness and stores the time the display
|
// wakeDisplay sets the display brightness back to config.DisplayMaxBrightness and stores the time the display
|
||||||
// last woke, ready for displayTimeoutTick to put the display back in the dim/off states.
|
// last woke, ready for displayTimeoutTick to put the display back in the dim/off states.
|
||||||
func wakeDisplay() {
|
// Set force to true to skip the backlight state check, this should be done if altering the tickers.
|
||||||
if backlightState == 0 {
|
func wakeDisplay(force bool) {
|
||||||
|
if backlightState == 0 && !force {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.DisplayMaxBrightness == 0 {
|
|
||||||
config.DisplayMaxBrightness = 100
|
|
||||||
}
|
|
||||||
|
|
||||||
err := setDisplayBrightness(config.DisplayMaxBrightness)
|
err := setDisplayBrightness(config.DisplayMaxBrightness)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("display wake failed, %s\n", err)
|
fmt.Printf("display wake failed, %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dim_ticker.Reset(time.Duration(config.DisplayDimAfterMs) * time.Millisecond)
|
if config.DisplayDimAfterSec == 0 {
|
||||||
off_ticker.Reset(time.Duration(config.DisplayOffAfterMs) * time.Millisecond)
|
dim_ticker.Stop()
|
||||||
|
} else {
|
||||||
|
dim_ticker.Reset(time.Duration(config.DisplayDimAfterSec) * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.DisplayOffAfterSec == 0 {
|
||||||
|
off_ticker.Stop()
|
||||||
|
} else {
|
||||||
|
off_ticker.Reset(time.Duration(config.DisplayOffAfterSec) * time.Second)
|
||||||
|
}
|
||||||
backlightState = 0
|
backlightState = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +198,43 @@ func watchTsEvents() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wakeDisplay()
|
wakeDisplay(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// startBacklightTickers starts the two tickers for dimming and switching off the display
|
||||||
|
// if they're not already set. This is done separately to the init routine as the "never dim"
|
||||||
|
// option has the value set to zero, but time.NewTicker only accept positive values.
|
||||||
|
func startBacklightTickers() {
|
||||||
|
LoadConfig()
|
||||||
|
if dim_ticker == nil && config.DisplayDimAfterSec != 0 {
|
||||||
|
fmt.Printf("display: dim_ticker has started.")
|
||||||
|
dim_ticker = time.NewTicker(time.Duration(config.DisplayDimAfterSec) * time.Second)
|
||||||
|
defer dim_ticker.Stop()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-dim_ticker.C:
|
||||||
|
tick_displayDim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if off_ticker == nil && config.DisplayOffAfterSec != 0 {
|
||||||
|
fmt.Printf("display: off_ticker has started.")
|
||||||
|
off_ticker = time.NewTicker(time.Duration(config.DisplayOffAfterSec) * time.Second)
|
||||||
|
defer off_ticker.Stop()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-off_ticker.C:
|
||||||
|
tick_displayOff()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,28 +246,11 @@ func init() {
|
||||||
updateStaticContents()
|
updateStaticContents()
|
||||||
displayInited = true
|
displayInited = true
|
||||||
fmt.Println("display inited")
|
fmt.Println("display inited")
|
||||||
wakeDisplay()
|
wakeDisplay(false)
|
||||||
requestDisplayUpdate()
|
requestDisplayUpdate()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
startBacklightTickers()
|
||||||
LoadConfig()
|
|
||||||
// Start display auto-sleeping tickers
|
|
||||||
dim_ticker = time.NewTicker(time.Duration(config.DisplayDimAfterMs) * time.Millisecond)
|
|
||||||
defer dim_ticker.Stop()
|
|
||||||
|
|
||||||
off_ticker = time.NewTicker(time.Duration(config.DisplayOffAfterMs) * time.Millisecond)
|
|
||||||
defer off_ticker.Stop()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-dim_ticker.C:
|
|
||||||
tick_displayDim()
|
|
||||||
case <-off_ticker.C:
|
|
||||||
tick_displayOff()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go watchTsEvents()
|
go watchTsEvents()
|
||||||
}
|
}
|
||||||
|
|
33
jsonrpc.go
33
jsonrpc.go
|
@ -225,13 +225,14 @@ func rpcTryUpdate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcSetBacklightSettings(data *BacklightSettings) error {
|
func rpcSetBacklightSettings(params BacklightSettings) error {
|
||||||
LoadConfig()
|
LoadConfig()
|
||||||
|
|
||||||
blConfig := *data
|
blConfig := params
|
||||||
|
|
||||||
if blConfig.MaxBrightness > 100 || blConfig.MaxBrightness < 0 {
|
// NOTE: by default, the frontend limits the brightness to 64, as that's what the device originally shipped with.
|
||||||
return fmt.Errorf("maxBrightness must be between 0 and 100")
|
if blConfig.MaxBrightness > 255 || blConfig.MaxBrightness < 0 {
|
||||||
|
return fmt.Errorf("maxBrightness must be between 0 and 255")
|
||||||
}
|
}
|
||||||
|
|
||||||
if blConfig.DimAfter < 0 {
|
if blConfig.DimAfter < 0 {
|
||||||
|
@ -243,12 +244,24 @@ func rpcSetBacklightSettings(data *BacklightSettings) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
config.DisplayMaxBrightness = blConfig.MaxBrightness
|
config.DisplayMaxBrightness = blConfig.MaxBrightness
|
||||||
config.DisplayDimAfterMs = int64(blConfig.DimAfter)
|
config.DisplayDimAfterSec = blConfig.DimAfter
|
||||||
config.DisplayOffAfterMs = int64(blConfig.OffAfter)
|
config.DisplayOffAfterSec = blConfig.OffAfter
|
||||||
|
|
||||||
if err := SaveConfig(); err != nil {
|
if err := SaveConfig(); err != nil {
|
||||||
return fmt.Errorf("failed to save config: %w", err)
|
return fmt.Errorf("failed to save config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("rpc: display: settings applied, max_brightness: %d, dim after: %ds, off after: %ds", config.DisplayMaxBrightness, config.DisplayDimAfterSec, config.DisplayOffAfterSec)
|
||||||
|
|
||||||
|
// If the device started up with auto-dim and/or auto-off set to zero, the display init
|
||||||
|
// method will not have started the tickers. So in case that has changed, attempt to start the tickers now.
|
||||||
|
startBacklightTickers()
|
||||||
|
|
||||||
|
// Wake the display after the settings are altered, this ensures the tickers
|
||||||
|
// are reset to the new settings, and will bring the display up to maxBrightness.
|
||||||
|
// Calling with force set to true, to ignore the current state of the display, and force
|
||||||
|
// it to reset the tickers.
|
||||||
|
wakeDisplay(true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,8 +270,8 @@ func rpcGetBacklightSettings() (*BacklightSettings, error) {
|
||||||
|
|
||||||
return &BacklightSettings{
|
return &BacklightSettings{
|
||||||
MaxBrightness: config.DisplayMaxBrightness,
|
MaxBrightness: config.DisplayMaxBrightness,
|
||||||
DimAfter: int(config.DisplayDimAfterMs),
|
DimAfter: int(config.DisplayDimAfterSec),
|
||||||
OffAfter: int(config.DisplayOffAfterMs),
|
OffAfter: int(config.DisplayOffAfterSec),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +435,7 @@ func callRPCHandler(handler RPCHandler, params map[string]interface{}) (interfac
|
||||||
}
|
}
|
||||||
args[i] = reflect.ValueOf(newStruct).Elem()
|
args[i] = reflect.ValueOf(newStruct).Elem()
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("invalid parameter type for: %s", paramName)
|
return nil, fmt.Errorf("invalid parameter type for: %s, type: %s", paramName, paramType.Kind())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
args[i] = convertedValue.Convert(paramType)
|
args[i] = convertedValue.Convert(paramType)
|
||||||
|
@ -597,6 +610,6 @@ var rpcHandlers = map[string]RPCHandler{
|
||||||
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
|
"getWakeOnLanDevices": {Func: rpcGetWakeOnLanDevices},
|
||||||
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
|
"setWakeOnLanDevices": {Func: rpcSetWakeOnLanDevices, Params: []string{"params"}},
|
||||||
"resetConfig": {Func: rpcResetConfig},
|
"resetConfig": {Func: rpcResetConfig},
|
||||||
"setBacklightSettings": {Func: rpcSetBacklightSettings, Params: []string{"settings"}},
|
"setBacklightSettings": {Func: rpcSetBacklightSettings, Params: []string{"params"}},
|
||||||
"getBacklightSettings": {Func: rpcGetBacklightSettings},
|
"getBacklightSettings": {Func: rpcGetBacklightSettings},
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,8 +230,18 @@ export default function SettingsSidebar() {
|
||||||
[send, setDeveloperMode],
|
[send, setDeveloperMode],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleBacklightSettingChange = useCallback((settings: BacklightSettings) => {
|
const handleBacklightSettingsChange = (settings: BacklightSettings) => {
|
||||||
send("setBacklightSettings", { settings }, resp => {
|
// If the user has set the display to dim after it turns off, set the dim_after
|
||||||
|
// value to never.
|
||||||
|
if (settings.dim_after > settings.off_after && settings.off_after != 0) {
|
||||||
|
settings.dim_after = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBacklightSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBacklightSettingsSave = () => {
|
||||||
|
send("setBacklightSettings", { params: settings.backlightSettings }, resp => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(
|
notifications.error(
|
||||||
`Failed to set backlight settings: ${resp.error.data || "Unknown error"}`,
|
`Failed to set backlight settings: ${resp.error.data || "Unknown error"}`,
|
||||||
|
@ -240,7 +250,7 @@ export default function SettingsSidebar() {
|
||||||
}
|
}
|
||||||
notifications.success("Backlight settings updated successfully");
|
notifications.success("Backlight settings updated successfully");
|
||||||
});
|
});
|
||||||
}, [send]);
|
};
|
||||||
|
|
||||||
const handleUpdateSSHKey = useCallback(() => {
|
const handleUpdateSSHKey = useCallback(() => {
|
||||||
send("setSSHKeyState", { sshKey }, resp => {
|
send("setSSHKeyState", { sshKey }, resp => {
|
||||||
|
@ -829,7 +839,6 @@ export default function SettingsSidebar() {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SettingsItem title="Display Brightness" description="Set the brightness of the display">
|
<SettingsItem title="Display Brightness" description="Set the brightness of the display">
|
||||||
{/* TODO: Allow the user to pick any value between 0 and 100 */}
|
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
label=""
|
label=""
|
||||||
|
@ -837,18 +846,65 @@ export default function SettingsSidebar() {
|
||||||
options={[
|
options={[
|
||||||
{ value: "0", label: "Off" },
|
{ value: "0", label: "Off" },
|
||||||
{ value: "10", label: "Low" },
|
{ value: "10", label: "Low" },
|
||||||
{ value: "50", label: "Medium" },
|
{ value: "35", label: "Medium" },
|
||||||
{ value: "100", label: "High" },
|
{ value: "64", label: "High" },
|
||||||
]}
|
]}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
handleBacklightSettingChange({
|
settings.backlightSettings.max_brightness = parseInt(e.target.value)
|
||||||
max_brightness: parseInt(e.target.value),
|
handleBacklightSettingsChange(settings.backlightSettings);
|
||||||
dim_after: settings.backlightSettings.dim_after,
|
|
||||||
off_after: settings.backlightSettings.off_after,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
{settings.backlightSettings.max_brightness != 0 && (
|
||||||
|
<>
|
||||||
|
<SettingsItem title="Dim Display After" description="Set how long to wait before dimming the display">
|
||||||
|
<SelectMenuBasic
|
||||||
|
size="SM"
|
||||||
|
label=""
|
||||||
|
value={settings.backlightSettings.dim_after.toString()}
|
||||||
|
options={[
|
||||||
|
{ value: "0", label: "Never" },
|
||||||
|
{ value: "60", label: "1 Minute" },
|
||||||
|
{ value: "300", label: "5 Minutes" },
|
||||||
|
{ value: "600", label: "10 Minutes" },
|
||||||
|
{ value: "1800", label: "30 Minutes" },
|
||||||
|
{ value: "3600", label: "1 Hour" },
|
||||||
|
]}
|
||||||
|
onChange={e => {
|
||||||
|
settings.backlightSettings.dim_after = parseInt(e.target.value)
|
||||||
|
handleBacklightSettingsChange(settings.backlightSettings);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
<SettingsItem title="Turn off Display After" description="Set how long to wait before turning off the display">
|
||||||
|
<SelectMenuBasic
|
||||||
|
size="SM"
|
||||||
|
label=""
|
||||||
|
value={settings.backlightSettings.off_after.toString()}
|
||||||
|
options={[
|
||||||
|
{ value: "0", label: "Never" },
|
||||||
|
{ value: "300", label: "5 Minutes" },
|
||||||
|
{ value: "600", label: "10 Minutes" },
|
||||||
|
{ value: "1800", label: "30 Minutes" },
|
||||||
|
{ value: "3600", label: "1 Hour" },
|
||||||
|
]}
|
||||||
|
onChange={e => {
|
||||||
|
settings.backlightSettings.off_after = parseInt(e.target.value)
|
||||||
|
handleBacklightSettingsChange(settings.backlightSettings);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<p className="text-xs text-slate-600 dark:text-slate-400">
|
||||||
|
The display will wake up when the connection state changes, or when touched.
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
size="SM"
|
||||||
|
theme="primary"
|
||||||
|
text="Save Display Settings"
|
||||||
|
onClick={handleBacklightSettingsSave}
|
||||||
|
/>
|
||||||
<div className="h-[1px] w-full bg-slate-800/10 dark:bg-slate-300/20" />
|
<div className="h-[1px] w-full bg-slate-800/10 dark:bg-slate-300/20" />
|
||||||
<div className="pb-2 space-y-4">
|
<div className="pb-2 space-y-4">
|
||||||
<SectionHeader
|
<SectionHeader
|
||||||
|
|
Loading…
Reference in New Issue