Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add devices dialog in editor #1765

Merged
merged 24 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0b5de50
feat: add devices dialog in editor WIP
meteyou Jan 26, 2024
267ab07
Merge branch 'develop' into feat/add-devices-dialog
meteyou Jan 26, 2024
a09c8fc
feat: add serial devices
meteyou Jan 26, 2024
2f108a5
refactor: refactor tabs and add can devices
meteyou Jan 26, 2024
9ffcf35
feat: add video devices
meteyou Jan 26, 2024
6493d13
feat: add filter to show csi cam, when libcamera array is empty
meteyou Jan 26, 2024
1facbd4
fix: fix identical keys for webcams
meteyou Jan 28, 2024
af1edd8
feat: display alt_name in v4l2 webcams when it is different to camera…
meteyou Jan 28, 2024
fd21ae2
Merge branch 'develop' into feat/add-devices-dialog
meteyou Jan 31, 2024
3628baf
refactor: refactor format and resolution output for libcameras
meteyou Jan 31, 2024
d5a76b3
locale: add LibcameraId to device path locale
meteyou Jan 31, 2024
72a9703
fix: fix margin between formats and resolutions
meteyou Jan 31, 2024
3d9aeb4
fix: fix copy to clipboard function
meteyou Jan 31, 2024
a8c098a
chore: update moonraker min version to v0.8.0-306
meteyou Feb 1, 2024
17bb02d
feat: add CAN support
meteyou Feb 1, 2024
e2632a4
fix: fix copy to clipboard function
meteyou Feb 1, 2024
9b5466c
feat: add warning to can tab
meteyou Feb 2, 2024
5c35ecc
feat: add format & resolutions to V4L2 video devices
meteyou Feb 4, 2024
f707286
Merge branch 'develop' into feat/add-devices-dialog
meteyou Feb 10, 2024
7288dd9
feat: add tooltip after copy function
meteyou Feb 11, 2024
cb079f1
feat: hide usb cams in libcamera list per default
meteyou Feb 14, 2024
8e3d65f
fix: only display formats & resolutions, when a mode exists in the ca…
meteyou Feb 14, 2024
b7b08c1
fix: fix class name in DevicesDialogVideoDeviceV4l2.vue
meteyou Feb 14, 2024
efe55d7
refactor: simplify filter rules
meteyou Feb 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/components/TheEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
:icon="isWriteable ? mdiFileDocumentEditOutline : mdiFileDocumentOutline"
:title="title">
<template #buttons>
<v-btn text tile class="d-none d-md-flex" @click="dialogDevices = true">
<v-icon small class="mr-1">{{ mdiUsb }}</v-icon>
{{ $t('Editor.DeviceDialog') }}
</v-btn>
<v-btn
v-if="restartServiceName === 'klipper'"
text
Expand Down Expand Up @@ -116,6 +120,7 @@
</v-card-actions>
</panel>
</v-dialog>
<devices-dialog :show-dialog="dialogDevices" @close="dialogDevices = false" />
</div>
</template>

Expand All @@ -135,14 +140,17 @@ import {
mdiHelp,
mdiHelpCircle,
mdiRestart,
mdiUsb,
} from '@mdi/js'
import type Codemirror from '@/components/inputs/Codemirror.vue'
import DevicesDialog from '@/components/dialogs/DevicesDialog.vue'

@Component({
components: { Panel, CodemirrorAsync },
components: { DevicesDialog, Panel, CodemirrorAsync },
})
export default class TheEditor extends Mixins(BaseMixin) {
private dialogConfirmChange = false
dialogConfirmChange = false
dialogDevices = false

formatFilesize = formatFilesize

Expand All @@ -157,6 +165,7 @@ export default class TheEditor extends Mixins(BaseMixin) {
mdiHelpCircle = mdiHelpCircle
mdiFileDocumentEditOutline = mdiFileDocumentEditOutline
mdiFileDocumentOutline = mdiFileDocumentOutline
mdiUsb = mdiUsb

private scrollbarOptions = { scrollbars: { autoHide: 'never' } }

Expand Down
111 changes: 111 additions & 0 deletions src/components/dialogs/DevicesDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<template>
<v-dialog :value="showDialog" width="500" persistent :fullscreen="isMobile">
<panel
id="devices-dialog"
:title="$t('DevicesDialog.Headline')"
:icon="mdiUsb"
card-class="devices-dialog"
:margin-bottom="false"
style="overflow: hidden"
:height="isMobile ? 0 : 548">
<template #buttons>
<v-menu :left="true" :offset-y="true" :close-on-content-click="false" attach="#devices-dialog">
<template #activator="{ on, attrs }">
<v-btn icon tile v-bind="attrs" v-on="on">
<v-icon small>{{ mdiCog }}</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item class="minHeight36">
<v-checkbox
v-model="hideSystemEntries"
class="mt-0"
hide-details
:label="$t('DevicesDialog.HideSystemEntries')" />
</v-list-item>
</v-list>
</v-menu>
<v-btn icon tile @click="closePrompt">
<v-icon>{{ mdiCloseThick }}</v-icon>
</v-btn>
</template>
<v-tabs v-model="tab" fixed-tabs>
<v-tab v-for="tab in tabs" :key="tab.tab">{{ tab.title }}</v-tab>
</v-tabs>
<overlay-scrollbars style="max-height: 400px; overflow-x: hidden">
<v-tabs-items v-model="tab">
<v-tab-item v-for="canInterface in canInterfaces" :key="canInterface">
<devices-dialog-can :hide-system-entries="hideSystemEntries" :name="canInterface" />
</v-tab-item>
<v-tab-item key="serial">
<devices-dialog-serial :hide-system-entries="hideSystemEntries" />
</v-tab-item>
<v-tab-item key="usb">
<devices-dialog-usb :hide-system-entries="hideSystemEntries" />
</v-tab-item>
<v-tab-item key="video">
<devices-dialog-video :hide-system-entries="hideSystemEntries" />
</v-tab-item>
</v-tabs-items>
</overlay-scrollbars>
</panel>
</v-dialog>
</template>

<script lang="ts">
import { Component, Mixins, Prop } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import Panel from '@/components/ui/Panel.vue'

import { mdiCog, mdiCloseThick, mdiUsb } from '@mdi/js'

@Component({
components: { Panel },
})
export default class DevicesDialog extends Mixins(BaseMixin) {
mdiCog = mdiCog
mdiUsb = mdiUsb
mdiCloseThick = mdiCloseThick

tab = 'serial'
hideSystemEntries = true

@Prop({ type: Boolean, default: false }) showDialog!: boolean

get tabs() {
const output: { tab: string; title: string }[] = [
{
tab: 'serial',
title: 'Serial',
},
{
tab: 'usb',
title: 'USB',
},
{
tab: 'video',
title: 'Video',
},
]

this.canInterfaces.forEach((name) => {
output.push({
tab: name,
title: name.toUpperCase(),
})
})

return output.sort((a, b) => a.title.localeCompare(b.title))
}

get canInterfaces() {
return Object.keys(this.$store.state.server.system_info?.canbus ?? {})
}

closePrompt() {
this.$emit('close')
}
}
</script>

<style scoped></style>
84 changes: 84 additions & 0 deletions src/components/dialogs/DevicesDialogCan.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<template>
<overlay-scrollbars style="max-height: 400px; overflow-x: hidden">
<v-card-text>
<v-row>
<v-col class="text-center">
<v-btn :loading="loading" color="primary" @click="refresh">{{ $t('DevicesDialog.Refresh') }}</v-btn>
</v-col>
</v-row>
<v-row v-if="devices.length" class="mt-0">
<v-col>
<devices-dialog-can-device v-for="device in devices" :key="device.uuid" :device="device" />
</v-col>
</v-row>
<v-row v-else-if="loaded" class="mt-0">
<v-col class="col-8 mx-auto">
<p class="text-center text--disabled mb-0">{{ $t('DevicesDialog.NoDeviceFound') }}</p>
</v-col>
</v-row>
<v-row v-else class="mt-0">
<v-col class="col-8 mx-auto">
<p class="text-center text--disabled mb-0">{{ $t('DevicesDialog.ClickRefresh') }}</p>
</v-col>
</v-row>
<v-row v-if="devices.length === 0">
<v-col>
<v-alert dense outlined type="info" :icon="mdiInformationVariantCircle">
{{ $t('DevicesDialog.CanBusInfo') }}
<v-row class="my-0">
<v-col class="text-center">
<v-btn
href="https://docs.mainsail.xyz/overview/features/query-devices#can-devices"
color="info"
outlined
text
small>
open guide
</v-btn>
</v-col>
</v-row>
</v-alert>
</v-col>
</v-row>
</v-card-text>
</overlay-scrollbars>
</template>

<script lang="ts">
import { Component, Mixins, Prop } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import DevicesDialogCanDevice from '@/components/dialogs/DevicesDialogCanDevice.vue'
import { mdiInformationVariantCircle } from '@mdi/js'

export interface CanDevice {
application: string
uuid: string
}

@Component({
components: { DevicesDialogCanDevice },
})
export default class DevicesDialogCan extends Mixins(BaseMixin) {
mdiInformationVariantCircle = mdiInformationVariantCircle

devices: CanDevice[] = []
loading = false
loaded = false

@Prop({ type: String, required: true }) name!: string
@Prop({ type: Boolean, default: false }) hideSystemEntries!: boolean

async refresh() {
this.loading = true

this.devices = await fetch(`${this.apiUrl}/machine/peripherals/canbus?interface=${this.name}`)
.then((res) => res.json())
.then((res) => res.result.can_uuids ?? [])

this.loading = false
this.loaded = true
}
}
</script>

<style scoped></style>
32 changes: 32 additions & 0 deletions src/components/dialogs/DevicesDialogCanDevice.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<template>
<v-card outlined class="mt-3 w-100">
<v-card-text>
<v-row>
<v-col>
<div class="text-overline mb-2 d-flex flex-row">
<span>{{ device.application }}</span>
</div>
</v-col>
</v-row>
<v-row class="mt-0">
<v-col>
<textfield-with-copy label="UUID" :value="device.uuid" />
</v-col>
</v-row>
</v-card-text>
</v-card>
</template>

<script lang="ts">
import { Component, Mixins, Prop } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { CanDevice } from '@/components/dialogs/DevicesDialogCan.vue'
import TextfieldWithCopy from '@/components/inputs/TextfieldWithCopy.vue'

@Component({
components: { TextfieldWithCopy },
})
export default class DevicesDialogCanDevice extends Mixins(BaseMixin) {
@Prop({ type: Object, required: true }) device!: CanDevice
}
</script>
72 changes: 72 additions & 0 deletions src/components/dialogs/DevicesDialogSerial.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<template>
<v-card-text>
<v-row>
<v-col class="text-center">
<v-btn :loading="loading" color="primary" @click="refresh">{{ $t('DevicesDialog.Refresh') }}</v-btn>
</v-col>
</v-row>
<v-row v-if="filteredDevices.length" class="mt-0">
<v-col>
<v-expansion-panels accordion>
<devices-dialog-serial-device
v-for="device in filteredDevices"
:key="device.path_by_hardware ?? device.device_path"
:device="device" />
</v-expansion-panels>
</v-col>
</v-row>
<v-row v-else-if="loaded" class="mt-0">
<v-col class="col-8 mx-auto">
<p class="text-center text--disabled mb-0">{{ $t('DevicesDialog.NoDeviceFound') }}</p>
</v-col>
</v-row>
<v-row v-else class="mt-0">
<v-col class="col-8 mx-auto">
<p class="text-center text--disabled mb-0">{{ $t('DevicesDialog.ClickRefresh') }}</p>
</v-col>
</v-row>
</v-card-text>
</template>

<script lang="ts">
import { Component, Mixins, Prop } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'

export interface SerialDevice {
device_type: 'hardware_uart' | 'usb'
device_path: string
device_name: string
driver_name: string
path_by_hardware?: string
path_by_id?: string
usb_location?: string
}

@Component
export default class DevicesDialogSerial extends Mixins(BaseMixin) {
devices: SerialDevice[] = []
loading = false
loaded = false

@Prop({ type: Boolean, default: false }) hideSystemEntries!: boolean

get filteredDevices() {
if (!this.hideSystemEntries) return this.devices

return this.devices.filter((device) => device.device_type !== 'hardware_uart')
}

async refresh() {
this.loading = true

this.devices = await fetch(this.apiUrl + '/machine/peripherals/serial')
.then((res) => res.json())
.then((res) => res.result?.serial_devices ?? [])

this.loading = false
this.loaded = true
}
}
</script>

<style scoped></style>
45 changes: 45 additions & 0 deletions src/components/dialogs/DevicesDialogSerialDevice.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<v-card outlined class="mt-3 w-100">
<v-list-item three-line>
<v-list-item-content>
<div class="text-overline mb-2 d-flex flex-row">
<span>{{ device.device_type.toUpperCase().replaceAll('_', ' ') }}</span>
<v-spacer />
<span>{{ device.driver_name }}</span>
</div>
<v-list-item-title class="text-h5 mb-0">{{ device.device_name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-card-text>
<v-row>
<v-col>
<textfield-with-copy :label="$t('DevicesDialog.DevicePath')" :value="device.device_path" />
</v-col>
</v-row>
<v-row v-if="device.path_by_id ?? false">
<v-col>
<textfield-with-copy :label="$t('DevicesDialog.PathById')" :value="device.path_by_id" />
</v-col>
</v-row>
<v-row v-if="device.path_by_hardware ?? false">
<v-col>
<textfield-with-copy :label="$t('DevicesDialog.PathByHardware')" :value="device.path_by_hardware" />
</v-col>
</v-row>
</v-card-text>
</v-card>
</template>

<script lang="ts">
import { Component, Mixins, Prop } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { SerialDevice } from '@/components/dialogs/DevicesDialogSerial.vue'
import TextfieldWithCopy from '@/components/inputs/TextfieldWithCopy.vue'

@Component({
components: { TextfieldWithCopy },
})
export default class DevicesDialogSerialDevice extends Mixins(BaseMixin) {
@Prop({ type: Object, required: true }) device!: SerialDevice
}
</script>
Loading
Loading