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: spoolman support #1119

Merged
merged 52 commits into from
Aug 6, 2023
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
6d22621
feat(spoolman): implement initial stores
matmen Jul 7, 2023
160ee27
feat(spoolman): implement support check, spool selection
matmen Jul 9, 2023
c8af0d2
fix(spoolman): handle all socket events
matmen Jul 9, 2023
32e8d0d
feat(spoolman): check if enough filament on spool
matmen Jul 9, 2023
d836e74
fix(spoolman): fix metadata fetching
matmen Jul 12, 2023
9c43318
refactor(spoolman): remove unnecessary imports
matmen Jul 12, 2023
1ddcef3
feat(spoolman): manage spools btn, change spool tool, misc
matmen Jul 12, 2023
d086872
feat: moar spoolman
matmen Jul 14, 2023
5739a1d
fix: round remaining weight
matmen Jul 14, 2023
3ae92d1
feat: qr code scanning
matmen Jul 22, 2023
fbc612e
fix: don't warn about missing file meta on spool change
matmen Jul 22, 2023
76d4807
refactor: filterResults
matmen Jul 22, 2023
d2356a3
fix: only consider img/video tagged cam sources
matmen Jul 22, 2023
a4d5fe8
refactor: show howto when no status present
matmen Jul 23, 2023
7ae6d00
fix: set canvas dimensions appropriately
matmen Jul 23, 2023
f2c4d56
fix: limit scan attempt rate
matmen Jul 23, 2023
5c3e66d
fix: full width qr scan modal on mobile
matmen Jul 23, 2023
edb666c
chore: swap scan code / manage spools button locations
matmen Jul 23, 2023
9ced66f
fix: multicam button margin
matmen Jul 23, 2023
e83a737
feat: mismatched filament material warning
matmen Jul 23, 2023
ed1b9c8
fix: hide archived spools
matmen Jul 23, 2023
f92d660
fix: move to dashboard on print start through spool selection
matmen Jul 23, 2023
fff5b3d
fix: only subtract already printed length when print is ongoing
matmen Jul 25, 2023
1dd8ca6
docs: document spoolman
matmen Jul 25, 2023
a84b765
fix: don't show fullscreen icon in camera embed
matmen Jul 25, 2023
accfa61
feat(settings): "Show spool selection dialog on print start" setting
matmen Jul 25, 2023
0a93870
refactor: move settings into own component
matmen Jul 26, 2023
afc3b93
upside down smile
matmen Jul 26, 2023
af1d6a7
refactor: switch to QrScanner
matmen Jul 26, 2023
832fc2f
fix: only warn for material mismatch if file has material metadata
matmen Jul 27, 2023
27304a6
fix: layout migration
matmen Jul 27, 2023
2b74cbd
feat: spoolman dashboard card
matmen Jul 27, 2023
f07783c
refactor: Remaining Weight -> Remaining
matmen Jul 27, 2023
13355ee
refactor: spoolman card
matmen Jul 27, 2023
30aeafd
fix lint
matmen Jul 27, 2023
830783f
feat(SpoolmanCard): show color as header banner on small screens
matmen Jul 27, 2023
562333f
fix: invalid state mutation
matmen Jul 28, 2023
a4f7951
refactor: reflect changes from #1129
matmen Jul 28, 2023
db03190
refactor: update spool id on open
matmen Jul 28, 2023
0d6d026
feat: clear filter if QR-selected spool isn't in filtered results
matmen Jul 28, 2023
8f650ce
fix: types, value formatting
matmen Jul 30, 2023
6472589
fix: component colors
matmen Jul 30, 2023
3a47ca8
Merge branch 'develop' into feat/spoolman
pedrolamas Jul 31, 2023
794e6c8
fix: remove no longer needed types
matmen Aug 1, 2023
28404b3
fix: scrollable v-dialog
matmen Aug 1, 2023
a0f38b4
fix: remove outdated engine prop
matmen Aug 1, 2023
3301aa0
fix: only register listeners if handled upstream
matmen Aug 1, 2023
530df90
fix: fall back to "-" on falsy values
matmen Aug 1, 2023
18c6cae
docs: reflect latest changes
matmen Aug 1, 2023
735684e
Merge branch 'develop' into feat/spoolman
pedrolamas Aug 4, 2023
f6f0609
comment out QR scanning related options
matmen Aug 4, 2023
460886f
Merge branch 'develop' into feat/spoolman
matmen Aug 4, 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
Binary file added docs/assets/images/spoolman-dashboard-card.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/images/spoolman-scan-spool.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 44 additions & 0 deletions docs/features/spoolman.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
layout: default
title: Spool Management
parent: Features
nav_order: 17
permalink: /features/spoolman
---

# Spool Management
{: .no_toc }

## Table of contents
{: .no_toc .text-delta }

1. TOC
{:toc}

---

Fluidd offers support for the [Spoolman](https://github.com/Donkie/Spoolman) filament spool manager.

### Print start
On print start, Fluidd will show a modal asking you to select the spool you want to use for printing.
The modal shows all available (i.e. not archived) spools.
A spool can either be selected by selecting it directly, or by scanning an associated QR code using an attached webcam.

![screenshot](/assets/images/spoolman-scan-spool.png)

Automatically opening the spool selection modal can be disabled from the Fluidd settings.

### Dashboard card
The currently selected spool and its metadata is shown in the Spoolman dashboard card.

#### Selecting a different spool
If you need to select another spool during your print (e.g. when your current spool has run out, or you have a multicolor print),
you can do so through the "Change Spool" button in the dashboard card.

![screenshot](/assets/images/spoolman-dashboard-card.png)

### Sanity checks
When starting a print or changing spools, Fluidd will automatically perform these sanity checks and warn you if they fail:
1) a spool is selected
2) the selected spool has enough filament left on it to finish the print job
3) the selected spool's filament type matches the one selected in the slicer
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ firmware.
- [Print history](/features/print_history)
- [Version management](/updates/automated) and upgrades
- Utilization graphs
- Filament [spool management](/features/spoolman)

## Supporting Fluidd

Expand Down
27 changes: 27 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"monaco-textmate": "^3.0.1",
"onigasm": "^2.2.5",
"panzoom": "^9.4.3",
"qr-scanner": "^1.4.2",
"qrcode.vue": "^1.7.0",
"semver": "^7.5.4",
"shlex": "^2.1.2",
Expand Down
3 changes: 3 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@

<file-system-download-dialog />
<updating-dialog />
<spool-selection-dialog />
</v-main>

<app-footer />
Expand All @@ -89,6 +90,7 @@ import FilesMixin from '@/mixins/files'
import BrowserMixin from '@/mixins/browser'
import { LinkPropertyHref } from 'vue-meta'
import FileSystemDownloadDialog from '@/components/widgets/filesystem/FileSystemDownloadDialog.vue'
import SpoolSelectionDialog from '@/components/widgets/spoolman/SpoolSelectionDialog.vue'

@Component<App>({
metaInfo () {
Expand All @@ -104,6 +106,7 @@ import FileSystemDownloadDialog from '@/components/widgets/filesystem/FileSystem
}
},
components: {
SpoolSelectionDialog,
FileSystemDownloadDialog
}
})
Expand Down
27 changes: 27 additions & 0 deletions src/api/socketActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,5 +702,32 @@ export const SocketActions = {
dispatch: 'webcams/onWebcamsList'
}
)
},

async spoolmanState () {
baseEmit(
'server.spoolman.get_spool_id', {
dispatch: 'spoolman/onActiveSpool'
}
)

baseEmit(
'server.spoolman.proxy', {
params: {
request_method: 'GET',
path: '/v1/spool'
},
dispatch: 'spoolman/onAvailableSpools'
}
)
},

async spoolmanSetSpool (spoolId: number | undefined) {
baseEmit(
'server.spoolman.post_spool_id', {
params: { spool_id: spoolId },
dispatch: 'spoolman/onActiveSpool'
}
)
}
}
5 changes: 5 additions & 0 deletions src/components/layout/AppSettingsNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default class AppSettingsNav extends Vue {
{ name: this.$t('app.setting.title.thermal_presets'), hash: '#presets', visible: true },
{ name: this.$t('app.setting.title.gcode_preview'), icon: '$cubeScan', hash: '#gcodePreview', visible: true },
{ name: this.$t('app.general.title.timelapse'), hash: '#timelapse', visible: this.supportsTimelapse },
{ name: this.$t('app.spoolman.title.spoolman'), hash: '#spoolman', visible: this.supportsSpoolman },
{ name: this.$t('app.version.title'), hash: '#versions', visible: this.supportsVersions }
]
}
Expand All @@ -56,5 +57,9 @@ export default class AppSettingsNav extends Vue {
get supportsTimelapse () {
return this.$store.getters['server/componentSupport']('timelapse')
}

get supportsSpoolman () {
return this.$store.getters['spoolman/getSupported']
}
}
</script>
100 changes: 100 additions & 0 deletions src/components/settings/SpoolmanSettings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<template>
<div>
<v-subheader id="spoolman">
{{ $t('app.spoolman.title.spoolman') }}
</v-subheader>
<v-card
:elevation="5"
dense
class="mb-4"
>
<app-setting
:title="$t('app.spoolman.setting.show_spool_selection_dialog_on_print_start')"
>
<v-switch
v-model="autoSpoolSelectionDialog"
hide-details
class="mt-0 mb-4"
/>
</app-setting>

<v-divider />
<app-setting
:title="$tc('app.spoolman.setting.auto_open_qr_camera')"
>
<v-select
v-model="autoOpenQRDetectionCameraId"
filled
dense
single-line
hide-details="auto"
:items="supportedCameras"
/>
</app-setting>

<v-divider />
<app-setting :title="$t('app.setting.label.reset')">
<app-btn
outlined
small
color="primary"
@click="handleReset"
>
{{ $t('app.setting.btn.reset') }}
</app-btn>
</app-setting>
</v-card>
</div>
</template>

<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator'
import { defaultState } from '@/store/config/state'
import StateMixin from '@/mixins/state'
import { CameraConfig } from '@/store/cameras/types'

@Component({
components: {}
})
export default class SpoolmanSettings extends Mixins(StateMixin) {
get autoSpoolSelectionDialog (): boolean {
return this.$store.state.config.uiSettings.spoolman.autoSpoolSelectionDialog
}

set autoSpoolSelectionDialog (value: boolean) {
this.$store.dispatch('config/saveByPath', {
path: 'uiSettings.spoolman.autoSpoolSelectionDialog',
value,
server: true
})
}

get supportedCameras () {
return [
{ text: this.$tc('app.setting.label.none', 0), value: null },
...this.$store.getters['cameras/getEnabledCameras']
.map((camera: CameraConfig) => ({ text: camera.name, value: camera.id, disabled: !camera.enabled || camera.service === 'iframe' }))
]
}

get autoOpenQRDetectionCameraId (): string {
return this.$store.state.config.uiSettings.spoolman.autoOpenQRDetectionCamera
}

set autoOpenQRDetectionCameraId (value: string) {
this.$store.dispatch('config/saveByPath', {
path: 'uiSettings.spoolman.autoOpenQRDetectionCamera',
value,
server: true
})
}

handleReset () {
this.$store.dispatch('config/saveByPath', {
path: 'uiSettings.spoolman',
value: defaultState().uiSettings.spoolman,
server: true
})
}
}
</script>
4 changes: 4 additions & 0 deletions src/components/settings/ToolheadSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,10 @@ export default class ToolHeadSettings extends Mixins(ToolheadMixin) {
return this.$store.getters['printer/getPrinterSettings']('force_move.enable_force_move') ?? false
}

get printerSupportsSpoolman () {
return this.$store.getters['spoolman/getSupported']
}

get showManualProbeDialogAutomatically () {
return this.$store.state.config.uiSettings.general.showManualProbeDialogAutomatically
}
Expand Down
28 changes: 27 additions & 1 deletion src/components/widgets/camera/CameraItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<template v-if="cameraComponent">
<component
:is="cameraComponent"
ref="component-instance"
:camera="camera"
class="camera-image"
@raw-camera-url="rawCameraUrl = $event"
Expand Down Expand Up @@ -53,10 +54,11 @@
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
import { Component, Vue, Prop, Watch, Ref } from 'vue-property-decorator'
import { CameraConfig } from '@/store/cameras/types'
import { CameraFullscreenAction } from '@/store/config/types'
import { CameraComponents } from '@/dynamicImports'
import CameraMixin from '@/mixins/camera'

@Component({})
export default class CameraItem extends Vue {
Expand All @@ -66,9 +68,33 @@ export default class CameraItem extends Vue {
@Prop({ type: Boolean, required: false, default: false })
readonly fullscreen!: boolean

@Ref('component-instance')
readonly componentInstance!: CameraMixin

rawCameraUrl: string | null = null
framesPerSecond : string | null = null

mounted () {
if (this.$listeners?.frame) {
if (this.componentInstance.streamingElement instanceof HTMLImageElement) {
this.componentInstance.streamingElement.addEventListener('load', () => this.handleFrame())
} else if (this.componentInstance.streamingElement instanceof HTMLVideoElement) {
this.handleFrame(true)
}
}
}

handleFrame (animate = false) {
const element = this.componentInstance?.streamingElement as HTMLImageElement | HTMLVideoElement
if (element) {
this.$emit('frame', element)
}

if (animate) {
requestAnimationFrame(() => this.handleFrame(this.componentInstance.animating))
}
}

@Watch('camera')
onCamera () {
this.rawCameraUrl = ''
Expand Down
1 change: 1 addition & 0 deletions src/components/widgets/camera/services/HlsstreamCamera.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
ref="streamingElement"
autoplay
muted
crossorigin="anonymous"
:style="cameraStyle"
/>
</template>
Expand Down
1 change: 1 addition & 0 deletions src/components/widgets/camera/services/IpstreamCamera.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
:src="cameraVideoSource"
autoplay
muted
crossorigin="anonymous"
:style="cameraStyle"
/>
</template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
ref="streamingElement"
:src="cameraImageSource"
:style="cameraStyle"
crossorigin="anonymous"
@load="handleImageLoad"
>
</template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
ref="streamingElement"
:src="cameraImageSource"
:style="cameraStyle"
crossorigin="anonymous"
>
</template>

Expand Down
Loading