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 spoolman support #1542

Merged
merged 35 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a00259f
feat: add first init process of spoolman
meteyou Aug 30, 2023
d8bffe6
feat: add spoolman panel on dashboard if moonraker component exists
meteyou Aug 30, 2023
818be40
feat: add icon for spool
meteyou Sep 2, 2023
215aea8
WIP
meteyou Sep 5, 2023
2c0ccb0
WIP
meteyou Sep 7, 2023
31be81f
WIP
meteyou Sep 8, 2023
475e31e
WIP
meteyou Sep 10, 2023
9bf10c6
WIP
meteyou Sep 11, 2023
0026126
refactor: change format for spool details format
meteyou Sep 12, 2023
9bd4787
feat: add option to remove spool
meteyou Sep 12, 2023
093dc21
refactor: remove submenu for change/eject spool and add a prompt to e…
meteyou Sep 14, 2023
11370b5
refactor: remove div in SpoolmanEjectSpoolDialog.vue
meteyou Sep 14, 2023
c07b44b
WIP
meteyou Sep 14, 2023
45e4f16
feat: autorefresh rest weight of spool
meteyou Sep 15, 2023
24d7e6c
refactor: also convert to kg in change spool dialog total weight
meteyou Sep 15, 2023
9ed824e
refactor: round remaining weight in spoolman panel
meteyou Sep 15, 2023
c10cc4a
WIP
meteyou Sep 15, 2023
d069329
locale: add headline to locale
meteyou Sep 17, 2023
b6fc068
locale: fix locale
meteyou Sep 17, 2023
bad9181
WIP
meteyou Sep 19, 2023
b4ef4a6
feat: add warnings in the print start dialog
meteyou Sep 19, 2023
3d27588
Merge branch 'develop' into feat/add-spoolman
meteyou Sep 19, 2023
c0414f2
Merge branch 'develop' into feat/add-spoolman
meteyou Sep 22, 2023
c478235
refactor: better usability on mobile devices in change spool dialog
meteyou Sep 24, 2023
fc0af95
feat: add option to click on the spool or name to open change spool d…
meteyou Oct 1, 2023
df4cd3a
feat: add option to click on the spool or name to open change spool d…
meteyou Oct 1, 2023
abcd497
refactor: remove unused getters in SpoolmanPanel.vue
meteyou Oct 3, 2023
c3d2723
refactor: remove filter for spools
meteyou Oct 3, 2023
43c40a7
refactor: remove testing true for timelapse
meteyou Oct 3, 2023
97643eb
fix: change margin bottom when timelapse exists
meteyou Oct 3, 2023
5a90fae
feat: reset search on opening change spool dialog
meteyou Oct 3, 2023
b076fb1
Merge branch 'develop' into feat/add-spoolman
meteyou Oct 3, 2023
4635026
refactor: use remaining_weight instead of calculating it
meteyou Oct 3, 2023
fd715ce
refactor: remove debug output
meteyou Oct 4, 2023
fff13b8
feat: add sort to spool dialog
meteyou Oct 6, 2023
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
7 changes: 7 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -277,13 +277,20 @@ export default class App extends Mixins(BaseMixin) {
@Watch('print_percent')
print_percentChanged(newVal: number): void {
this.drawFavicon(newVal)
this.refreshSpoolman()
}

@Watch('printerIsPrinting')
printerIsPrintingChanged(): void {
this.drawFavicon(this.print_percent)
}

refreshSpoolman(): void {
if (this.moonrakerComponents.includes('spoolman')) {
this.$store.dispatch('server/spoolman/refreshActiveSpool', null, { root: true })
}
}

appHeight() {
this.$nextTick(() => {
const doc = document.documentElement
Expand Down
174 changes: 174 additions & 0 deletions src/components/dialogs/SpoolmanChangeSpoolDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<template>
<div>
<v-dialog v-model="showDialog" width="800" persistent :fullscreen="isMobile">
<panel
:title="$t('Panels.SpoolmanPanel.ChangeSpool')"
:icon="mdiAdjust"
card-class="spoolman-change-spool-dialog"
:margin-bottom="false">
<template #buttons>
<v-btn icon tile @click="close">
<v-icon>{{ mdiCloseThick }}</v-icon>
</v-btn>
</template>
<v-card-title>
<v-text-field
v-model="search"
:append-icon="mdiMagnify"
:label="$t('Panels.SpoolmanPanel.Search')"
outlined
dense
hide-details
style="max-width: 300px" />
<v-spacer />
<v-btn
:title="$t('Panels.SpoolmanPanel.Refresh')"
class="px-2 minwidth-0 ml-3"
:loading="loadings.includes('refreshSpools')"
@click="refreshSpools">
<v-icon>{{ mdiRefresh }}</v-icon>
</v-btn>
<v-btn
:title="$t('Panels.SpoolmanPanel.OpenSpoolManager')"
class="px-2 minwidth-0 ml-3"
@click="openSpoolManager">
<v-icon>{{ mdiDatabase }}</v-icon>
</v-btn>
</v-card-title>
<v-card-text class="px-0 pb-0">
<v-data-table
:headers="headers"
:items="spools"
item-key="id"
:search="search"
sort-by="last_used"
:sort-desc="true"
:custom-filter="customFilter">
<template #no-data>
<div class="text-center">{{ $t('Panels.SpoolmanPanel.NoSpools') }}</div>
</template>
<template #no-results>
<div class="text-center">{{ $t('Panels.SpoolmanPanel.NoResults') }}</div>
</template>

<template #item="{ item }">
<SpoolmanChangeSpoolDialogRow :key="item.id" :spool="item" @set-spool="setSpool" />
</template>
</v-data-table>
</v-card-text>
</panel>
</v-dialog>
</div>
</template>

<script lang="ts">
import Component from 'vue-class-component'
import { Mixins, Prop, Watch } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import Panel from '@/components/ui/Panel.vue'
import { mdiCloseThick, mdiAdjust, mdiDatabase, mdiMagnify, mdiRefresh } from '@mdi/js'
import { ServerSpoolmanStateSpool } from '@/store/server/spoolman/types'
import SpoolmanChangeSpoolDialogRow from '@/components/dialogs/SpoolmanChangeSpoolDialogRow.vue'
@Component({
components: { SpoolmanChangeSpoolDialogRow, Panel },
})
export default class SpoolmanChangeSpoolDialog extends Mixins(BaseMixin) {
mdiAdjust = mdiAdjust
mdiCloseThick = mdiCloseThick
mdiDatabase = mdiDatabase
mdiMagnify = mdiMagnify
mdiRefresh = mdiRefresh

@Prop({ required: true }) declare readonly showDialog: boolean

search = ''

get spools(): ServerSpoolmanStateSpool[] {
return this.$store.state.server.spoolman.spools ?? []
}

get headers() {
return [
{
text: ' ',
align: 'start',
sortable: false,
},
{
text: this.$t('Panels.SpoolmanPanel.Filament'),
align: 'start',
value: 'filament.name',
sortable: false,
},
{
text: this.$t('Panels.SpoolmanPanel.Material'),
align: 'center',
value: 'filament.material',
},
{
text: this.$t('Panels.SpoolmanPanel.LastUsed'),
align: 'end',
value: 'last_used',
},
{
text: this.$t('Panels.SpoolmanPanel.Weight'),
align: 'end',
value: 'remaining_weight',
},
]
}

get spoolManagerUrl() {
return this.$store.state.server.config.config?.spoolman?.server ?? null
}

openSpoolManager() {
window.open(this.spoolManagerUrl, '_blank')
}

mounted() {
this.refresh()
}

refresh() {
this.$store.dispatch('server/spoolman/refreshSpools')
}

close() {
this.$emit('close')
}

refreshSpools() {
this.$store.dispatch('server/spoolman/refreshSpools')
}

customFilter(value: any, search: string, item: ServerSpoolmanStateSpool): boolean {
const querySplits = search.toLowerCase().split(' ')
const searchArray = [
item.comment,
item.filament.name,
item.filament.vendor.name,
item.filament.material,
item.location,
]

for (const query of querySplits) {
const result = searchArray.some((q) => q?.toLowerCase().includes(query))

if (!result) return false
}

return true
}

setSpool(spool: ServerSpoolmanStateSpool) {
this.$store.dispatch('server/spoolman/setActiveSpool', spool.id)
this.close()
}

@Watch('showDialog')
onShowDialogChanged(newVal: boolean) {
if (newVal) this.search = ''
}
}
</script>
104 changes: 104 additions & 0 deletions src/components/dialogs/SpoolmanChangeSpoolDialogRow.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<template>
<tr class="cursor-pointer" @click="setSpoolRow">
<td style="width: 50px" class="pr-0 py-2">
<spool-icon :color="color" style="width: 50px; float: left" class="mr-3" />
</td>
<td class="py-2" style="min-width: 300px">
<strong class="text-no-wrap">{{ vendor }} - {{ name }}</strong>
<template v-if="location">
<br />
{{ $t('Panels.SpoolmanPanel.Location') }}: {{ location }}
</template>
<template v-if="spool.comment">
<br />
{{ spool.comment }}
</template>
</td>
<td class="text-center text-no-wrap">{{ material }}</td>
<td class="text-right text-no-wrap">{{ last_used }}</td>
<td class="text-right text-no-wrap">
<strong>{{ remaining_weight_format }}</strong>
<small class="ml-1">/ {{ total_weight_format }}</small>
</td>
</tr>
</template>

<script lang="ts">
import Component from 'vue-class-component'
import { Mixins, Prop } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import { ServerSpoolmanStateSpool } from '@/store/server/spoolman/types'
@Component({})
export default class SpoolmanChangeSpoolDialogRow extends Mixins(BaseMixin) {
@Prop({ required: true }) declare readonly spool: ServerSpoolmanStateSpool
get color() {
const color = this.spool.filament?.color_hex ?? '000'

return `#${color}`
}

get vendor() {
return this.spool.filament?.vendor?.name ?? 'Unknown'
}

get name() {
return this.spool.filament?.name ?? 'Unknown'
}

get location() {
return this.spool.location
}

get material() {
return this.spool.filament?.material ?? '--'
}

get remaining_weight() {
return this.spool.remaining_weight ?? 0
}

get total_weight() {
return this.spool.filament?.weight ?? 0
}

get remaining_weight_format() {
return `${this.remaining_weight.toFixed(0)}g`
}

get total_weight_format() {
meteyou marked this conversation as resolved.
Show resolved Hide resolved
if (this.total_weight < 1000) {
return `${this.total_weight.toFixed(0)}g`
}

let totalRound = Math.round(this.total_weight / 1000)
if (totalRound !== this.total_weight / 1000) {
totalRound = Math.round(this.total_weight / 100) / 10
}

return `${totalRound}kg`
}

get last_used() {
const last_used = this.spool.last_used ?? null
if (!last_used) return this.$t('Panels.SpoolmanPanel.Never')

const date = new Date(this.spool.last_used)
const now = new Date()
const diff = now.getTime() - date.getTime()

if (diff <= 1000 * 60 * 60 * 24) return this.$t('Panels.SpoolmanPanel.Today')
if (diff <= 1000 * 60 * 60 * 24 * 2) return this.$t('Panels.SpoolmanPanel.Yesterday')
if (diff <= 1000 * 60 * 60 * 24 * 14) {
const days = Math.floor(diff / (1000 * 60 * 60 * 24))

return this.$t('Panels.SpoolmanPanel.DaysAgo', { days })
}

return date.toLocaleDateString()
}

setSpoolRow() {
this.$emit('set-spool', this.spool)
}
}
</script>
55 changes: 55 additions & 0 deletions src/components/dialogs/SpoolmanEjectSpoolDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<v-dialog v-model="showDialog" width="400" persistent :fullscreen="isMobile">
<panel
:title="$t('Panels.SpoolmanPanel.EjectSpool')"
:icon="mdiEject"
card-class="spoolman-eject-spool-dialog"
:margin-bottom="false">
<template #buttons>
<v-btn icon tile @click="close">
<v-icon>{{ mdiCloseThick }}</v-icon>
</v-btn>
</template>
<v-card-text>
<v-row>
<v-col>
<p class="body-2">{{ $t('Panels.SpoolmanPanel.EjectSpoolQuestion') }}</p>
</v-col>
</v-row>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn text @click="close">{{ $t('Panels.SpoolmanPanel.Cancel') }}</v-btn>
<v-btn color="primary" text @click="removeSpool">
{{ $t('Panels.SpoolmanPanel.EjectSpool') }}
</v-btn>
</v-card-actions>
</panel>
</v-dialog>
</template>

<script lang="ts">
import Component from 'vue-class-component'
import { Mixins, Prop } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import Panel from '@/components/ui/Panel.vue'
import { mdiCloseThick, mdiEject } from '@mdi/js'
@Component({
components: { Panel },
})
export default class SpoolmanEjectSpoolDialog extends Mixins(BaseMixin) {
mdiEject = mdiEject
mdiCloseThick = mdiCloseThick

@Prop({ required: true }) declare readonly showDialog: boolean

close() {
this.$emit('close')
}

removeSpool() {
this.$store.dispatch('server/spoolman/setActiveSpool', 0)
this.close()
}
}
</script>
Loading
Loading