mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-03-15 23:46:51 +08:00
feat(atx): merge serial relay support from #223
This commit is contained in:
@@ -34,7 +34,9 @@ const activeTab = ref('atx')
|
||||
|
||||
// ATX state
|
||||
const powerState = ref<'on' | 'off' | 'unknown'>('unknown')
|
||||
const confirmAction = ref<'short' | 'long' | 'reset' | null>(null)
|
||||
// Decouple action data from dialog visibility to prevent race conditions
|
||||
const pendingAction = ref<'short' | 'long' | 'reset' | null>(null)
|
||||
const confirmDialogOpen = ref(false)
|
||||
|
||||
// WOL state
|
||||
const wolMacAddress = ref('')
|
||||
@@ -58,15 +60,21 @@ const powerStateText = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
function requestAction(action: 'short' | 'long' | 'reset') {
|
||||
pendingAction.value = action
|
||||
confirmDialogOpen.value = true
|
||||
}
|
||||
|
||||
function handleAction() {
|
||||
if (confirmAction.value === 'short') emit('powerShort')
|
||||
else if (confirmAction.value === 'long') emit('powerLong')
|
||||
else if (confirmAction.value === 'reset') emit('reset')
|
||||
confirmAction.value = null
|
||||
console.log('[AtxPopover] Confirming action:', pendingAction.value)
|
||||
if (pendingAction.value === 'short') emit('powerShort')
|
||||
else if (pendingAction.value === 'long') emit('powerLong')
|
||||
else if (pendingAction.value === 'reset') emit('reset')
|
||||
confirmDialogOpen.value = false
|
||||
}
|
||||
|
||||
const confirmTitle = computed(() => {
|
||||
switch (confirmAction.value) {
|
||||
switch (pendingAction.value) {
|
||||
case 'short': return t('atx.confirmShortTitle')
|
||||
case 'long': return t('atx.confirmLongTitle')
|
||||
case 'reset': return t('atx.confirmResetTitle')
|
||||
@@ -75,7 +83,7 @@ const confirmTitle = computed(() => {
|
||||
})
|
||||
|
||||
const confirmDescription = computed(() => {
|
||||
switch (confirmAction.value) {
|
||||
switch (pendingAction.value) {
|
||||
case 'short': return t('atx.confirmShortDesc')
|
||||
case 'long': return t('atx.confirmLongDesc')
|
||||
case 'reset': return t('atx.confirmResetDesc')
|
||||
@@ -178,7 +186,7 @@ watch(
|
||||
variant="outline"
|
||||
size="sm"
|
||||
class="w-full justify-start gap-2 h-8 text-xs"
|
||||
@click="confirmAction = 'short'"
|
||||
@click="requestAction('short')"
|
||||
>
|
||||
<Power class="h-3.5 w-3.5" />
|
||||
{{ t('atx.shortPress') }}
|
||||
@@ -188,7 +196,7 @@ watch(
|
||||
variant="outline"
|
||||
size="sm"
|
||||
class="w-full justify-start gap-2 h-8 text-xs text-orange-600 hover:text-orange-700 hover:bg-orange-50 dark:hover:bg-orange-950"
|
||||
@click="confirmAction = 'long'"
|
||||
@click="requestAction('long')"
|
||||
>
|
||||
<CircleDot class="h-3.5 w-3.5" />
|
||||
{{ t('atx.longPress') }}
|
||||
@@ -198,7 +206,7 @@ watch(
|
||||
variant="outline"
|
||||
size="sm"
|
||||
class="w-full justify-start gap-2 h-8 text-xs text-red-600 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-950"
|
||||
@click="confirmAction = 'reset'"
|
||||
@click="requestAction('reset')"
|
||||
>
|
||||
<RotateCcw class="h-3.5 w-3.5" />
|
||||
{{ t('atx.reset') }}
|
||||
@@ -260,7 +268,7 @@ watch(
|
||||
</div>
|
||||
|
||||
<!-- Confirm Dialog -->
|
||||
<AlertDialog :open="!!confirmAction" @update:open="confirmAction = null">
|
||||
<AlertDialog :open="confirmDialogOpen" @update:open="confirmDialogOpen = $event">
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{{ confirmTitle }}</AlertDialogTitle>
|
||||
|
||||
@@ -118,6 +118,8 @@ export enum AtxDriverType {
|
||||
Gpio = "gpio",
|
||||
/** USB HID relay module */
|
||||
UsbRelay = "usbrelay",
|
||||
/** Serial/COM port relay (LCUS type) */
|
||||
Serial = "serial",
|
||||
/** Disabled / Not configured */
|
||||
None = "none",
|
||||
}
|
||||
@@ -151,6 +153,8 @@ export interface AtxKeyConfig {
|
||||
pin: number;
|
||||
/** Active level (only applicable to GPIO, ignored for USB Relay) */
|
||||
active_level: ActiveLevel;
|
||||
/** Baud rate for serial relay (start with 9600) */
|
||||
baud_rate: number;
|
||||
}
|
||||
|
||||
/** LED sensing configuration (optional) */
|
||||
@@ -411,6 +415,7 @@ export interface AppConfig {
|
||||
export interface AtxKeyConfigUpdate {
|
||||
driver?: AtxDriverType;
|
||||
device?: string;
|
||||
baud_rate?: number;
|
||||
pin?: number;
|
||||
active_level?: ActiveLevel;
|
||||
}
|
||||
@@ -439,6 +444,8 @@ export interface AtxConfigUpdate {
|
||||
/** Available ATX devices for discovery */
|
||||
export interface AtxDevices {
|
||||
/** Available GPIO chips (/dev/gpiochip*) */
|
||||
/** Available Serial ports (/dev/ttyUSB*) */
|
||||
serial_ports: string[];
|
||||
gpio_chips: string[];
|
||||
/** Available USB HID relay devices (/dev/hidraw*) */
|
||||
usb_relays: string[];
|
||||
@@ -681,4 +688,3 @@ export interface WebConfigUpdate {
|
||||
bind_address?: string;
|
||||
https_enabled?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -598,12 +598,14 @@ const atxConfig = ref({
|
||||
device: '',
|
||||
pin: 0,
|
||||
active_level: 'high' as ActiveLevel,
|
||||
baud_rate: 9600,
|
||||
},
|
||||
reset: {
|
||||
driver: 'none' as AtxDriverType,
|
||||
device: '',
|
||||
pin: 0,
|
||||
active_level: 'high' as ActiveLevel,
|
||||
baud_rate: 9600,
|
||||
},
|
||||
led: {
|
||||
enabled: false,
|
||||
@@ -618,6 +620,7 @@ const atxConfig = ref({
|
||||
const atxDevices = ref<AtxDevices>({
|
||||
gpio_chips: [],
|
||||
usb_relays: [],
|
||||
serial_ports: [],
|
||||
})
|
||||
|
||||
// Encoder backend
|
||||
@@ -1175,12 +1178,14 @@ async function saveAtxConfig() {
|
||||
device: atxConfig.value.power.device || undefined,
|
||||
pin: atxConfig.value.power.pin,
|
||||
active_level: atxConfig.value.power.active_level,
|
||||
baud_rate: atxConfig.value.power.baud_rate,
|
||||
},
|
||||
reset: {
|
||||
driver: atxConfig.value.reset.driver,
|
||||
device: atxConfig.value.reset.device || undefined,
|
||||
pin: atxConfig.value.reset.pin,
|
||||
active_level: atxConfig.value.reset.active_level,
|
||||
baud_rate: atxConfig.value.reset.baud_rate,
|
||||
},
|
||||
led: {
|
||||
enabled: atxConfig.value.led.enabled,
|
||||
@@ -1202,6 +1207,8 @@ async function saveAtxConfig() {
|
||||
function getAtxDevicesForDriver(driver: string): string[] {
|
||||
if (driver === 'gpio') {
|
||||
return atxDevices.value.gpio_chips
|
||||
} else if (driver === 'serial') {
|
||||
return atxDevices.value.serial_ports
|
||||
} else if (driver === 'usbrelay') {
|
||||
return atxDevices.value.usb_relays
|
||||
}
|
||||
@@ -2474,6 +2481,7 @@ watch(() => config.value.hid_backend, async () => {
|
||||
<option value="none">{{ t('settings.atxDriverNone') }}</option>
|
||||
<option value="gpio">{{ t('settings.atxDriverGpio') }}</option>
|
||||
<option value="usbrelay">{{ t('settings.atxDriverUsbRelay') }}</option>
|
||||
<option value="serial">Serial (LCUS)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
@@ -2486,7 +2494,7 @@ watch(() => config.value.hid_backend, async () => {
|
||||
</div>
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<div class="space-y-2">
|
||||
<Label for="power-pin">{{ atxConfig.power.driver === 'usbrelay' ? t('settings.atxChannel') : t('settings.atxPin') }}</Label>
|
||||
<Label for="power-pin">{{ ['usbrelay', 'serial'].includes(atxConfig.power.driver) ? t('settings.atxChannel') : t('settings.atxPin') }}</Label>
|
||||
<Input id="power-pin" type="number" v-model.number="atxConfig.power.pin" min="0" :disabled="atxConfig.power.driver === 'none'" />
|
||||
</div>
|
||||
<div v-if="atxConfig.power.driver === 'gpio'" class="space-y-2">
|
||||
@@ -2496,6 +2504,16 @@ watch(() => config.value.hid_backend, async () => {
|
||||
<option value="low">{{ t('settings.atxLevelLow') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="atxConfig.power.driver === 'serial'" class="space-y-2">
|
||||
<Label for="power-baudrate">{{ t('settings.baudRate') }}</Label>
|
||||
<select id="power-baudrate" v-model.number="atxConfig.power.baud_rate" class="w-full h-9 px-3 rounded-md border border-input bg-background text-sm">
|
||||
<option :value="9600">9600</option>
|
||||
<option :value="19200">19200</option>
|
||||
<option :value="38400">38400</option>
|
||||
<option :value="57600">57600</option>
|
||||
<option :value="115200">115200</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -2514,6 +2532,7 @@ watch(() => config.value.hid_backend, async () => {
|
||||
<option value="none">{{ t('settings.atxDriverNone') }}</option>
|
||||
<option value="gpio">{{ t('settings.atxDriverGpio') }}</option>
|
||||
<option value="usbrelay">{{ t('settings.atxDriverUsbRelay') }}</option>
|
||||
<option value="serial">Serial (LCUS)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
@@ -2526,7 +2545,7 @@ watch(() => config.value.hid_backend, async () => {
|
||||
</div>
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<div class="space-y-2">
|
||||
<Label for="reset-pin">{{ atxConfig.reset.driver === 'usbrelay' ? t('settings.atxChannel') : t('settings.atxPin') }}</Label>
|
||||
<Label for="reset-pin">{{ ['usbrelay', 'serial'].includes(atxConfig.reset.driver) ? t('settings.atxChannel') : t('settings.atxPin') }}</Label>
|
||||
<Input id="reset-pin" type="number" v-model.number="atxConfig.reset.pin" min="0" :disabled="atxConfig.reset.driver === 'none'" />
|
||||
</div>
|
||||
<div v-if="atxConfig.reset.driver === 'gpio'" class="space-y-2">
|
||||
@@ -2536,6 +2555,16 @@ watch(() => config.value.hid_backend, async () => {
|
||||
<option value="low">{{ t('settings.atxLevelLow') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="atxConfig.reset.driver === 'serial'" class="space-y-2">
|
||||
<Label for="reset-baudrate">{{ t('settings.baudRate') }}</Label>
|
||||
<select id="reset-baudrate" v-model.number="atxConfig.reset.baud_rate" class="w-full h-9 px-3 rounded-md border border-input bg-background text-sm">
|
||||
<option :value="9600">9600</option>
|
||||
<option :value="19200">19200</option>
|
||||
<option :value="38400">38400</option>
|
||||
<option :value="57600">57600</option>
|
||||
<option :value="115200">115200</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user