mirror of https://github.com/jetkvm/kvm.git
Compare commits
9 Commits
fbee0b2925
...
3cef992e36
| Author | SHA1 | Date |
|---|---|---|
|
|
3cef992e36 | |
|
|
eeacceb667 | |
|
|
802166ba23 | |
|
|
4bc60c3f1b | |
|
|
e7e6d7cb9d | |
|
|
65bbcf85ad | |
|
|
79098d3546 | |
|
|
50fc88aae1 | |
|
|
204909b49a |
14
audio.go
14
audio.go
|
|
@ -22,7 +22,7 @@ var (
|
||||||
audioLogger zerolog.Logger
|
audioLogger zerolog.Logger
|
||||||
currentAudioTrack *webrtc.TrackLocalStaticSample
|
currentAudioTrack *webrtc.TrackLocalStaticSample
|
||||||
inputTrackHandling atomic.Bool
|
inputTrackHandling atomic.Bool
|
||||||
useUSBForAudioOutput bool
|
useUSBForAudioOutput atomic.Bool
|
||||||
audioOutputEnabled atomic.Bool
|
audioOutputEnabled atomic.Bool
|
||||||
audioInputEnabled atomic.Bool
|
audioInputEnabled atomic.Bool
|
||||||
)
|
)
|
||||||
|
|
@ -32,7 +32,7 @@ func initAudio() {
|
||||||
|
|
||||||
// Load audio output source from config
|
// Load audio output source from config
|
||||||
ensureConfigLoaded()
|
ensureConfigLoaded()
|
||||||
useUSBForAudioOutput = config.AudioOutputSource == "usb"
|
useUSBForAudioOutput.Store(config.AudioOutputSource == "usb")
|
||||||
|
|
||||||
// Enable both by default
|
// Enable both by default
|
||||||
audioOutputEnabled.Store(true)
|
audioOutputEnabled.Store(true)
|
||||||
|
|
@ -57,7 +57,7 @@ func startAudio() error {
|
||||||
// Start output audio if not running and enabled
|
// Start output audio if not running and enabled
|
||||||
if outputSource == nil && audioOutputEnabled.Load() {
|
if outputSource == nil && audioOutputEnabled.Load() {
|
||||||
alsaDevice := "hw:0,0" // HDMI
|
alsaDevice := "hw:0,0" // HDMI
|
||||||
if useUSBForAudioOutput {
|
if useUSBForAudioOutput.Load() {
|
||||||
alsaDevice = "hw:1,0" // USB
|
alsaDevice = "hw:1,0" // USB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,16 +167,17 @@ func SetAudioOutputSource(useUSB bool) error {
|
||||||
audioMutex.Lock()
|
audioMutex.Lock()
|
||||||
defer audioMutex.Unlock()
|
defer audioMutex.Unlock()
|
||||||
|
|
||||||
if useUSBForAudioOutput == useUSB {
|
if useUSBForAudioOutput.Load() == useUSB {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
audioLogger.Info().
|
audioLogger.Info().
|
||||||
Bool("old_usb", useUSBForAudioOutput).
|
Bool("old_usb", useUSBForAudioOutput.Load()).
|
||||||
Bool("new_usb", useUSB).
|
Bool("new_usb", useUSB).
|
||||||
Msg("Switching audio output source")
|
Msg("Switching audio output source")
|
||||||
|
|
||||||
useUSBForAudioOutput = useUSB
|
oldValue := useUSBForAudioOutput.Load()
|
||||||
|
useUSBForAudioOutput.Store(useUSB)
|
||||||
|
|
||||||
ensureConfigLoaded()
|
ensureConfigLoaded()
|
||||||
if useUSB {
|
if useUSB {
|
||||||
|
|
@ -186,6 +187,7 @@ func SetAudioOutputSource(useUSB bool) error {
|
||||||
}
|
}
|
||||||
if err := SaveConfig(); err != nil {
|
if err := SaveConfig(); err != nil {
|
||||||
audioLogger.Error().Err(err).Msg("Failed to save config")
|
audioLogger.Error().Err(err).Msg("Failed to save config")
|
||||||
|
useUSBForAudioOutput.Store(oldValue)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -305,11 +305,11 @@ func wakeDisplay(force bool, reason string) {
|
||||||
displayLogger.Warn().Err(err).Msg("failed to wake display")
|
displayLogger.Warn().Err(err).Msg("failed to wake display")
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.DisplayDimAfterSec != 0 {
|
if config.DisplayDimAfterSec != 0 && dimTicker != nil {
|
||||||
dimTicker.Reset(time.Duration(config.DisplayDimAfterSec) * time.Second)
|
dimTicker.Reset(time.Duration(config.DisplayDimAfterSec) * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.DisplayOffAfterSec != 0 {
|
if config.DisplayOffAfterSec != 0 && offTicker != nil {
|
||||||
offTicker.Reset(time.Duration(config.DisplayOffAfterSec) * time.Second)
|
offTicker.Reset(time.Duration(config.DisplayOffAfterSec) * time.Second)
|
||||||
}
|
}
|
||||||
backlightState = 0
|
backlightState = 0
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ type OutputRelay struct {
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
logger zerolog.Logger
|
logger zerolog.Logger
|
||||||
running atomic.Bool
|
running atomic.Bool
|
||||||
sample media.Sample // Reusable sample for zero-allocation hot path
|
sample media.Sample
|
||||||
|
stopped chan struct{}
|
||||||
|
|
||||||
// Stats (Uint32: overflows after 2.7 years @ 50fps, faster atomics on 32-bit ARM)
|
// Stats (Uint32: overflows after 2.7 years @ 50fps, faster atomics on 32-bit ARM)
|
||||||
framesRelayed atomic.Uint32
|
framesRelayed atomic.Uint32
|
||||||
|
|
@ -38,8 +39,9 @@ func NewOutputRelay(source AudioSource, audioTrack *webrtc.TrackLocalStaticSampl
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
stopped: make(chan struct{}),
|
||||||
sample: media.Sample{
|
sample: media.Sample{
|
||||||
Duration: 20 * time.Millisecond, // Constant for all Opus frames
|
Duration: 20 * time.Millisecond,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,13 +57,15 @@ func (r *OutputRelay) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the relay
|
// Stop stops the relay and waits for goroutine to exit
|
||||||
func (r *OutputRelay) Stop() {
|
func (r *OutputRelay) Stop() {
|
||||||
if !r.running.Swap(false) {
|
if !r.running.Swap(false) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.cancel()
|
r.cancel()
|
||||||
|
<-r.stopped
|
||||||
|
|
||||||
r.logger.Debug().
|
r.logger.Debug().
|
||||||
Uint32("frames_relayed", r.framesRelayed.Load()).
|
Uint32("frames_relayed", r.framesRelayed.Load()).
|
||||||
Uint32("frames_dropped", r.framesDropped.Load()).
|
Uint32("frames_dropped", r.framesDropped.Load()).
|
||||||
|
|
@ -70,6 +74,8 @@ func (r *OutputRelay) Stop() {
|
||||||
|
|
||||||
// relayLoop continuously reads from audio source and writes to WebRTC
|
// relayLoop continuously reads from audio source and writes to WebRTC
|
||||||
func (r *OutputRelay) relayLoop() {
|
func (r *OutputRelay) relayLoop() {
|
||||||
|
defer close(r.stopped)
|
||||||
|
|
||||||
const reconnectDelay = 1 * time.Second
|
const reconnectDelay = 1 * time.Second
|
||||||
|
|
||||||
for r.running.Load() {
|
for r.running.Load() {
|
||||||
|
|
|
||||||
10
jsonrpc.go
10
jsonrpc.go
|
|
@ -899,7 +899,7 @@ func updateUsbRelatedConfig(wasAudioEnabled bool) error {
|
||||||
if config.UsbDevices != nil && !config.UsbDevices.Audio && config.AudioOutputSource == "usb" {
|
if config.UsbDevices != nil && !config.UsbDevices.Audio && config.AudioOutputSource == "usb" {
|
||||||
audioMutex.Lock()
|
audioMutex.Lock()
|
||||||
config.AudioOutputSource = "hdmi"
|
config.AudioOutputSource = "hdmi"
|
||||||
useUSBForAudioOutput = false
|
useUSBForAudioOutput.Store(false)
|
||||||
audioSourceChanged = true
|
audioSourceChanged = true
|
||||||
audioMutex.Unlock()
|
audioMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
@ -908,7 +908,7 @@ func updateUsbRelatedConfig(wasAudioEnabled bool) error {
|
||||||
if config.UsbDevices != nil && config.UsbDevices.Audio && !wasAudioEnabled {
|
if config.UsbDevices != nil && config.UsbDevices.Audio && !wasAudioEnabled {
|
||||||
audioMutex.Lock()
|
audioMutex.Lock()
|
||||||
config.AudioOutputSource = "usb"
|
config.AudioOutputSource = "usb"
|
||||||
useUSBForAudioOutput = true
|
useUSBForAudioOutput.Store(true)
|
||||||
audioSourceChanged = true
|
audioSourceChanged = true
|
||||||
audioMutex.Unlock()
|
audioMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
@ -970,8 +970,10 @@ func rpcSetUsbDeviceState(device string, enabled bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcGetAudioOutputSource() (string, error) {
|
func rpcGetAudioOutputSource() (string, error) {
|
||||||
ensureConfigLoaded()
|
if useUSBForAudioOutput.Load() {
|
||||||
return config.AudioOutputSource, nil
|
return "usb", nil
|
||||||
|
}
|
||||||
|
return "hdmi", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpcSetAudioOutputSource(source string) error {
|
func rpcSetAudioOutputSource(source string) error {
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ type Client struct {
|
||||||
var (
|
var (
|
||||||
defaultTimerDuration = 1 * time.Second
|
defaultTimerDuration = 1 * time.Second
|
||||||
defaultLinkUpTimeout = 30 * time.Second
|
defaultLinkUpTimeout = 30 * time.Second
|
||||||
|
defaultDHCPTimeout = 5 * time.Second // DHCP request timeout (not link up timeout)
|
||||||
maxRenewalAttemptDuration = 2 * time.Hour
|
maxRenewalAttemptDuration = 2 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -125,11 +126,11 @@ func NewClient(ctx context.Context, ifaces []string, c *Config, l *zerolog.Logge
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Timeout == 0 {
|
if cfg.Timeout == 0 {
|
||||||
cfg.Timeout = defaultLinkUpTimeout
|
cfg.Timeout = defaultDHCPTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Retries == 0 {
|
if cfg.Retries == 0 {
|
||||||
cfg.Retries = 3
|
cfg.Retries = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
|
|
@ -153,9 +154,15 @@ func NewClient(ctx context.Context, ifaces []string, c *Config, l *zerolog.Logge
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetTimer(t *time.Timer, l *zerolog.Logger) {
|
func resetTimer(t *time.Timer, attempt int, l *zerolog.Logger) {
|
||||||
l.Debug().Dur("delay", defaultTimerDuration).Msg("will retry later")
|
// Exponential backoff: 1s, 2s, 4s, 8s, max 8s
|
||||||
t.Reset(defaultTimerDuration)
|
backoffAttempt := attempt
|
||||||
|
if backoffAttempt > 3 {
|
||||||
|
backoffAttempt = 3
|
||||||
|
}
|
||||||
|
delay := time.Duration(1<<backoffAttempt) * time.Second
|
||||||
|
l.Debug().Dur("delay", delay).Int("attempt", attempt).Msg("will retry later")
|
||||||
|
t.Reset(delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRenewalTime(lease *Lease) time.Duration {
|
func getRenewalTime(lease *Lease) time.Duration {
|
||||||
|
|
@ -168,12 +175,14 @@ func getRenewalTime(lease *Lease) time.Duration {
|
||||||
|
|
||||||
func (c *Client) requestLoop(t *time.Timer, family int, ifname string) {
|
func (c *Client) requestLoop(t *time.Timer, family int, ifname string) {
|
||||||
l := c.l.With().Str("interface", ifname).Int("family", family).Logger()
|
l := c.l.With().Str("interface", ifname).Int("family", family).Logger()
|
||||||
|
attempt := 0
|
||||||
for range t.C {
|
for range t.C {
|
||||||
l.Info().Msg("requesting lease")
|
l.Info().Int("attempt", attempt).Msg("requesting lease")
|
||||||
|
|
||||||
if _, err := c.ensureInterfaceUp(ifname); err != nil {
|
if _, err := c.ensureInterfaceUp(ifname); err != nil {
|
||||||
l.Error().Err(err).Msg("failed to ensure interface up")
|
l.Error().Err(err).Int("attempt", attempt).Msg("failed to ensure interface up")
|
||||||
resetTimer(t, c.l)
|
resetTimer(t, attempt, c.l)
|
||||||
|
attempt++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,11 +197,14 @@ func (c *Client) requestLoop(t *time.Timer, family int, ifname string) {
|
||||||
lease, err = c.requestLease6(ifname)
|
lease, err = c.requestLease6(ifname)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Error().Err(err).Msg("failed to request lease")
|
l.Error().Err(err).Int("attempt", attempt).Msg("failed to request lease")
|
||||||
resetTimer(t, c.l)
|
resetTimer(t, attempt, c.l)
|
||||||
|
attempt++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Successfully obtained lease, reset attempt counter
|
||||||
|
attempt = 0
|
||||||
c.handleLeaseChange(lease)
|
c.handleLeaseChange(lease)
|
||||||
|
|
||||||
nextRenewal := getRenewalTime(lease)
|
nextRenewal := getRenewalTime(lease)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,31 @@ show_help() {
|
||||||
echo " $0 -r 192.168.0.17 -u admin"
|
echo " $0 -r 192.168.0.17 -u admin"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Function to check if device is pingable
|
||||||
|
check_ping() {
|
||||||
|
local host=$1
|
||||||
|
msg_info "▶ Checking if device is reachable at ${host}..."
|
||||||
|
if ! ping -c 3 -W 5 "${host}" > /dev/null 2>&1; then
|
||||||
|
msg_err "Error: Cannot reach device at ${host}"
|
||||||
|
msg_err "Please verify the IP address and network connectivity"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
msg_info "✓ Device is reachable"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if SSH is accessible
|
||||||
|
check_ssh() {
|
||||||
|
local user=$1
|
||||||
|
local host=$2
|
||||||
|
msg_info "▶ Checking SSH connectivity to ${user}@${host}..."
|
||||||
|
if ! ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ConnectTimeout=10 "${user}@${host}" "echo 'SSH connection successful'" > /dev/null 2>&1; then
|
||||||
|
msg_err "Error: Cannot establish SSH connection to ${user}@${host}"
|
||||||
|
msg_err "Please verify SSH access and credentials"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
msg_info "✓ SSH connection successful"
|
||||||
|
}
|
||||||
|
|
||||||
# Default values
|
# Default values
|
||||||
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
|
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
|
||||||
REMOTE_USER="root"
|
REMOTE_USER="root"
|
||||||
|
|
@ -113,6 +138,10 @@ if [ -z "$REMOTE_HOST" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check device connectivity before proceeding
|
||||||
|
check_ping "${REMOTE_HOST}"
|
||||||
|
check_ssh "${REMOTE_USER}" "${REMOTE_HOST}"
|
||||||
|
|
||||||
# check if the current CPU architecture is x86_64
|
# check if the current CPU architecture is x86_64
|
||||||
if [ "$(uname -m)" != "x86_64" ]; then
|
if [ "$(uname -m)" != "x86_64" ]; then
|
||||||
msg_warn "Warning: This script is only supported on x86_64 architecture"
|
msg_warn "Warning: This script is only supported on x86_64 architecture"
|
||||||
|
|
@ -147,10 +176,10 @@ if [ "$RUN_GO_TESTS" = true ]; then
|
||||||
make build_dev_test
|
make build_dev_test
|
||||||
|
|
||||||
msg_info "▶ Copying device-tests.tar.gz to remote host"
|
msg_info "▶ Copying device-tests.tar.gz to remote host"
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > /tmp/device-tests.tar.gz" < device-tests.tar.gz
|
||||||
|
|
||||||
msg_info "▶ Running go tests"
|
msg_info "▶ Running go tests"
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" ash << 'EOF'
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" ash << 'EOF'
|
||||||
set -e
|
set -e
|
||||||
TMP_DIR=$(mktemp -d)
|
TMP_DIR=$(mktemp -d)
|
||||||
cd ${TMP_DIR}
|
cd ${TMP_DIR}
|
||||||
|
|
@ -193,10 +222,10 @@ then
|
||||||
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
||||||
|
|
||||||
# Copy the binary to the remote host as if we were the OTA updater.
|
# Copy the binary to the remote host as if we were the OTA updater.
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/jetkvm/jetkvm_app.update" < bin/jetkvm_app
|
||||||
|
|
||||||
# Reboot the device, the new app will be deployed by the startup process.
|
# Reboot the device, the new app will be deployed by the startup process.
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "reboot"
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "reboot"
|
||||||
else
|
else
|
||||||
msg_info "▶ Building development binary"
|
msg_info "▶ Building development binary"
|
||||||
do_make build_dev \
|
do_make build_dev \
|
||||||
|
|
@ -205,21 +234,21 @@ else
|
||||||
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
ENABLE_SYNC_TRACE=${ENABLE_SYNC_TRACE}
|
||||||
|
|
||||||
# Kill any existing instances of the application
|
# Kill any existing instances of the application
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true"
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "killall jetkvm_app_debug || true"
|
||||||
|
|
||||||
# Copy the binary to the remote host
|
# Copy the binary to the remote host
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "cat > ${REMOTE_PATH}/jetkvm_app_debug" < bin/jetkvm_app
|
||||||
|
|
||||||
if [ "$RESET_USB_HID_DEVICE" = true ]; then
|
if [ "$RESET_USB_HID_DEVICE" = true ]; then
|
||||||
msg_info "▶ Resetting USB HID device"
|
msg_info "▶ Resetting USB HID device"
|
||||||
msg_warn "The option has been deprecated and will be removed in a future version, as JetKVM will now reset USB gadget configuration when needed"
|
msg_warn "The option has been deprecated and will be removed in a future version, as JetKVM will now reset USB gadget configuration when needed"
|
||||||
# Remove the old USB gadget configuration
|
# Remove the old USB gadget configuration
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*"
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "rm -rf /sys/kernel/config/usb_gadget/jetkvm/configs/c.1/hid.usb*"
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC"
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" "ls /sys/class/udc > /sys/kernel/config/usb_gadget/jetkvm/UDC"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Deploy and run the application on the remote host
|
# Deploy and run the application on the remote host
|
||||||
ssh "${REMOTE_USER}@${REMOTE_HOST}" ash << EOF
|
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no "${REMOTE_USER}@${REMOTE_HOST}" ash << EOF
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Set the library path to include the directory where librockit.so is located
|
# Set the library path to include the directory where librockit.so is located
|
||||||
|
|
@ -229,6 +258,17 @@ export LD_LIBRARY_PATH=/oem/usr/lib:\$LD_LIBRARY_PATH
|
||||||
killall jetkvm_app || true
|
killall jetkvm_app || true
|
||||||
killall jetkvm_app_debug || true
|
killall jetkvm_app_debug || true
|
||||||
|
|
||||||
|
# Wait until both binaries are killed, max 10 seconds
|
||||||
|
i=1
|
||||||
|
while [ \$i -le 10 ]; do
|
||||||
|
echo "Waiting for jetkvm_app and jetkvm_app_debug to be killed, \$i/10 ..."
|
||||||
|
if ! pgrep -f "jetkvm_app" > /dev/null && ! pgrep -f "jetkvm_app_debug" > /dev/null; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
i=\$((i + 1))
|
||||||
|
done
|
||||||
|
|
||||||
# Navigate to the directory where the binary will be stored
|
# Navigate to the directory where the binary will be stored
|
||||||
cd "${REMOTE_PATH}"
|
cd "${REMOTE_PATH}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,10 @@ export default function SettingsHardwareRoute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
setBacklightSettings(settings);
|
setBacklightSettings(settings);
|
||||||
handleBacklightSettingsSave();
|
handleBacklightSettingsSave(settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBacklightSettingsSave = () => {
|
const handleBacklightSettingsSave = (backlightSettings: BacklightSettings) => {
|
||||||
send("setBacklightSettings", { params: backlightSettings }, (resp: JsonRpcResponse) => {
|
send("setBacklightSettings", { params: backlightSettings }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(
|
notifications.error(
|
||||||
|
|
@ -81,7 +81,7 @@ export default function SettingsHardwareRoute() {
|
||||||
const duration = enabled ? 90 : -1;
|
const duration = enabled ? 90 : -1;
|
||||||
send("setVideoSleepMode", { duration }, (resp: JsonRpcResponse) => {
|
send("setVideoSleepMode", { duration }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.hardware_power_saving_failed_error({ error: resp.error.data ||m.unknown_error() }));
|
notifications.error(m.hardware_power_saving_failed_error({ error: resp.error.data || m.unknown_error() }));
|
||||||
setPowerSavingEnabled(!enabled); // Attempt to revert on error
|
setPowerSavingEnabled(!enabled); // Attempt to revert on error
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue