diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 5161a942..b3fc00c4 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -10,7 +10,7 @@ pub fn capabilities() -> PlatformCapabilities { encoder: FeatureCapability::available([ "software", "vaapi", "nvenc", "qsv", "amf", "rkmpp", "v4l2m2m", ]), - hid: FeatureCapability::available(["otg", "ch9329", "none"]), + hid: FeatureCapability::available(["otg", "ch9329", "bluetooth", "none"]), atx: FeatureCapability::available(["gpio", "usb_relay", "serial", "wol", "none"]), msd: FeatureCapability::available(["configfs"]), otg: FeatureCapability::available(["configfs"]), diff --git a/web/src/components/HidDriverDialog.vue b/web/src/components/HidDriverDialog.vue index 96953e07..cf2eff5b 100644 --- a/web/src/components/HidDriverDialog.vue +++ b/web/src/components/HidDriverDialog.vue @@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { useConfigStore } from '@/stores/config' +import { useSystemStore } from '@/stores/system' import { request } from '@/api/request' import type { HidConfig, MsdConfig } from '@/types/generated' import { useHidConnection } from '@/composables/useHidConnection' @@ -14,6 +15,9 @@ import HidDeviceOverview from './HidDeviceOverview.vue' const props = defineProps<{ dirty?: boolean }>() const emit = defineEmits<{ close: []; applied: []; discard: [] }>() const { t } = useI18n(), store = useConfigStore() +const systemStore = useSystemStore() +const otgSupported = computed(() => systemStore.platform?.otg.available === true) +const msdSupported = computed(() => systemStore.platform?.msd.available === true) const draft = ref(selectionFrom(store.hid)) const valid = ref(false), busy = ref(false), error = ref(''), applied = ref(false), loaded = ref(false) const acknowledged = ref(!props.dirty), uncertain = ref(false) @@ -52,7 +56,12 @@ async function refreshMsd() { return msd } async function refreshConfigs() { - const results = await Promise.allSettled([refreshHid(), refreshMsd(), readConfig<{ enabled: boolean }>('/config/otg-network'), readConfig<{ enabled: boolean }>('/config/uac')]) + const requests: Promise[] = [refreshHid()] + if (msdSupported.value) requests.push(refreshMsd()) + if (otgSupported.value) { + requests.push(readConfig<{ enabled: boolean }>('/config/otg-network'), readConfig<{ enabled: boolean }>('/config/uac')) + } + const results = await Promise.allSettled(requests) emit('applied') const failure = results.find(r => r.status === 'rejected') if (failure?.status === 'rejected') throw failure.reason @@ -115,9 +124,10 @@ async function close() { async function load() { loaded.value = false; error.value = '' try { + if (!systemStore.platform) await systemStore.fetchSystemInfo() await refreshHid() draft.value = selectionFrom(store.hid) - if (store.hid?.backend === 'otg') { + if (otgSupported.value && store.hid?.backend === 'otg') { const [msd, network, audio] = await Promise.all([refreshMsd(), readConfig<{ enabled: boolean }>('/config/otg-network'), readConfig<{ enabled: boolean }>('/config/uac')]) disabledUsb.value = [msd.enabled ? t('hidGuide.msd') : '', network.enabled ? t('hidGuide.network') : '', audio.enabled ? t('hidGuide.audio') : ''].filter(Boolean) } @@ -141,7 +151,7 @@ onMounted(load)