Skip to content

Commit be6c219

Browse files
feat: Adding AFC-Klipper-Add-On Card (#1751)
Signed-off-by: Jim Madill <[email protected]> Co-authored-by: Pedro Lamas <[email protected]>
1 parent d3d37a3 commit be6c219

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3286
-13
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<template>
2+
<collapsable-card
3+
:title="$t('app.afc.Headline')"
4+
icon="$afcIcon"
5+
draggable
6+
layout-path="dashboard.afc-card"
7+
>
8+
<template #menu>
9+
<afc-card-buttons />
10+
<afc-card-settings />
11+
</template>
12+
<v-card-text class="pt-1">
13+
<afc-card-message />
14+
<afc-card-bypass />
15+
<afc-card-extruder
16+
v-for="extruder in filteredExtruders"
17+
:key="extruder"
18+
:name="extruder"
19+
class="mt-3"
20+
/>
21+
<afc-card-unit
22+
v-for="unit in filteredUnits"
23+
:key="unit"
24+
:name="unit"
25+
class="mt-3"
26+
/>
27+
</v-card-text>
28+
</collapsable-card>
29+
</template>
30+
<script lang="ts">
31+
import { Component, Mixins } from 'vue-property-decorator'
32+
import AfcMixin from '@/mixins/afc'
33+
import AfcCardMessage from '@/components/widgets/afc/AfcCardMessage.vue'
34+
import AfcCardBypass from '@/components/widgets/afc/AfcCardBypass.vue'
35+
import AfcCardExtruder from '@/components/widgets/afc/AfcCardExtruder.vue'
36+
import AfcCardUnit from '@/components/widgets/afc/AfcCardUnit.vue'
37+
import AfcCardButtons from '@/components/widgets/afc/AfcCardButtons.vue'
38+
import AfcCardSettings from '@/components/widgets/afc/AfcCardSettings.vue'
39+
40+
@Component({
41+
components: {
42+
AfcCardMessage,
43+
AfcCardBypass,
44+
AfcCardExtruder,
45+
AfcCardUnit,
46+
AfcCardButtons,
47+
AfcCardSettings
48+
}
49+
})
50+
export default class AfcCard extends Mixins(AfcMixin) {
51+
get filteredExtruders (): string[] {
52+
return this.afcExtruders
53+
.filter(extruder => !this.afcHiddenExtruders.includes(extruder))
54+
}
55+
56+
get filteredUnits (): string[] {
57+
return this.afcUnits
58+
.filter(unit => !this.afcHiddenUnits.includes(unit))
59+
}
60+
}
61+
</script>
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
<template>
2+
<v-menu
3+
:offset-y="true"
4+
:close-on-content-click="false"
5+
:title="$t('app.afc.Functions')"
6+
left
7+
>
8+
<template #activator="{ on, attrs }">
9+
<app-btn
10+
icon
11+
v-bind="attrs"
12+
v-on="on"
13+
>
14+
<v-icon dense>
15+
$menu
16+
</v-icon>
17+
</app-btn>
18+
</template>
19+
<v-list dense>
20+
<v-list-item
21+
v-for="command in commands"
22+
:key="command.command"
23+
>
24+
<v-tooltip
25+
top
26+
:disabled="!command.description"
27+
>
28+
<template #activator="{ on, attrs }">
29+
<app-btn
30+
class="fill-width"
31+
:disabled="!klippyReady || command.disabled"
32+
small
33+
v-bind="attrs"
34+
v-on="on"
35+
@click="sendGcode(command.command)"
36+
>
37+
<v-icon
38+
v-if="command.icon"
39+
small
40+
left
41+
>
42+
{{ command.icon }}
43+
</v-icon>
44+
{{ command.text }}
45+
</app-btn>
46+
</template>
47+
<span>
48+
{{ command.description }}
49+
</span>
50+
</v-tooltip>
51+
</v-list-item>
52+
<v-list-item
53+
v-for="macro in macros"
54+
:key="macro.macroName"
55+
>
56+
<v-tooltip
57+
top
58+
:disabled="!macro.macro.description"
59+
>
60+
<template #activator="{ on, attrs }">
61+
<macro-btn
62+
v-bind="attrs"
63+
:macro="macro.macro"
64+
small
65+
class="fill-width"
66+
v-on="on"
67+
@click="sendGcode($event)"
68+
>
69+
{{ macro.text }}
70+
</macro-btn>
71+
</template>
72+
<span>
73+
{{ macro.macro.description }}
74+
</span>
75+
</v-tooltip>
76+
</v-list-item>
77+
<v-list-item>
78+
<app-btn
79+
class="fill-width"
80+
small
81+
@click="showAfcSettings = true"
82+
>
83+
<v-icon
84+
small
85+
left
86+
>
87+
$afcSettings
88+
</v-icon>
89+
{{ $t('app.afc.AfcSettings') }}
90+
</app-btn>
91+
<afc-settings-dialog v-model="showAfcSettings" />
92+
</v-list-item>
93+
<v-list-item>
94+
<app-btn
95+
class="fill-width"
96+
small
97+
@click="downloadDebugJson"
98+
>
99+
<v-icon
100+
small
101+
left
102+
>
103+
$afcDebugJson
104+
</v-icon>
105+
{{ $t('app.afc.DebugJson') }}
106+
</app-btn>
107+
</v-list-item>
108+
</v-list>
109+
</v-menu>
110+
</template>
111+
<script lang="ts">
112+
import { Component, Mixins } from 'vue-property-decorator'
113+
import StateMixin from '@/mixins/state'
114+
import AfcMixin from '@/mixins/afc'
115+
import MacroBtn from '@/components/widgets/macros/MacroBtn.vue'
116+
import AfcSettingsDialog from '@/components/widgets/afc/dialogs/AfcSettingsDialog.vue'
117+
import type { GcodeCommands, KlipperPrinterAfcSettings, KlipperPrinterConfig, KlipperPrinterSettings, KlipperPrinterState } from '@/store/printer/types'
118+
import downloadUrl from '@/util/download-url'
119+
import type { Macro } from '@/store/macros/types'
120+
121+
type AfcCommand = {
122+
icon: string,
123+
text: string,
124+
command: string,
125+
description?: string,
126+
disabled?: boolean
127+
}
128+
129+
type AfcMacro = {
130+
text: string,
131+
macroName: string,
132+
macro: Macro,
133+
disabled?: boolean
134+
}
135+
136+
@Component({
137+
components: {
138+
AfcSettingsDialog,
139+
MacroBtn
140+
}
141+
})
142+
export default class AfcCardButtons extends Mixins(StateMixin, AfcMixin) {
143+
showAfcSettings = false
144+
145+
get printerSettings (): KlipperPrinterSettings {
146+
return this.$typedGetters['printer/getPrinterSettings']
147+
}
148+
149+
get printerConfig (): KlipperPrinterConfig {
150+
return this.$typedGetters['printer/getPrinterConfig']
151+
}
152+
153+
get availableCommands (): GcodeCommands {
154+
return this.$typedGetters['printer/getAvailableCommands']
155+
}
156+
157+
get commands () {
158+
const availableCommands = this.availableCommands
159+
160+
const commands: AfcCommand[] = []
161+
162+
if ('AFC_CALIBRATION' in availableCommands) {
163+
commands.push({
164+
icon: '$afcCalibration',
165+
text: this.$t('app.afc.Calibrate').toString(),
166+
command: 'AFC_CALIBRATION',
167+
description: availableCommands['AFC_CALIBRATION'].help,
168+
disabled: this.printerPrinting
169+
})
170+
}
171+
172+
if (this.afc?.led_state === true) {
173+
if ('TURN_OFF_AFC_LED' in availableCommands) {
174+
commands.push({
175+
icon: '$afcTurnOffLed',
176+
text: this.$t('app.afc.LedOff').toString(),
177+
command: 'TURN_OFF_AFC_LED',
178+
description: availableCommands['TURN_OFF_AFC_LED'].help
179+
})
180+
}
181+
} else {
182+
if ('TURN_ON_AFC_LED' in availableCommands) {
183+
commands.push({
184+
icon: '$afcTurnOnLed',
185+
text: this.$t('app.afc.LedOn').toString(),
186+
command: 'TURN_ON_AFC_LED',
187+
description: availableCommands['TURN_ON_AFC_LED'].help
188+
})
189+
}
190+
}
191+
192+
if (
193+
this.afc?.td1_present === true &&
194+
'AFC_GET_TD_ONE_DATA' in availableCommands
195+
) {
196+
commands.push({
197+
icon: '',
198+
text: this.$t('app.afc.CaptureTd').toString(),
199+
command: 'AFC_GET_TD_ONE_DATA',
200+
description: availableCommands['AFC_GET_TD_ONE_DATA'].help,
201+
disabled: this.printerPrinting
202+
})
203+
}
204+
205+
return commands
206+
}
207+
208+
get macros () {
209+
const settings: KlipperPrinterAfcSettings | undefined = this.printerSettings.afc
210+
211+
const afcMacros: AfcMacro[] = []
212+
213+
if (settings?.wipe) {
214+
const macroName: string = settings.wipe_cmd || 'AFC_BRUSH'
215+
const macro: Macro | undefined = this.$typedGetters['macros/getMacroByName'](macroName)
216+
217+
if (macro != null) {
218+
afcMacros.push({
219+
text: this.$t('app.afc.BrushNozzle').toString(),
220+
macroName,
221+
macro,
222+
disabled: this.printerPrinting
223+
})
224+
}
225+
}
226+
227+
if (settings?.park) {
228+
const macroName: string = settings.park_cmd || 'AFC_PARK'
229+
const macro: Macro | undefined = this.$typedGetters['macros/getMacroByName'](macroName)
230+
231+
if (macro != null) {
232+
afcMacros.push({
233+
text: this.$t('app.afc.ParkNozzle').toString(),
234+
macroName,
235+
macro,
236+
disabled: this.printerPrinting
237+
})
238+
}
239+
}
240+
241+
return afcMacros
242+
}
243+
244+
downloadDebugJson () {
245+
const printer: KlipperPrinterState = this.$typedState.printer.printer
246+
247+
const output = {
248+
config: Object.fromEntries(
249+
Object.entries(this.printerConfig)
250+
.filter(([key]) => /^afc(?:$|_)/.test(key))
251+
),
252+
settings: Object.fromEntries(
253+
Object.entries(this.printerSettings)
254+
.filter(([key]) => /^afc(?:$|_)/.test(key))
255+
),
256+
printer: Object.fromEntries(
257+
Object.entries(printer)
258+
.filter(([key]) => /^afc(?:$|_)/.test(key))
259+
),
260+
}
261+
262+
const jsonString = JSON.stringify(output)
263+
const url = `data:text/plain;charset=utf-8,${encodeURIComponent(jsonString)}`
264+
265+
downloadUrl('afc_debug.json', url)
266+
}
267+
}
268+
</script>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<template>
2+
<v-alert
3+
v-if="bypassState"
4+
type="warning"
5+
class="mt-3"
6+
dense
7+
text
8+
>
9+
{{ $t('app.afc.BypassActive') }}
10+
</v-alert>
11+
</template>
12+
<script lang="ts">
13+
import { Component, Mixins } from 'vue-property-decorator'
14+
import StateMixin from '@/mixins/state'
15+
import AfcMixin from '@/mixins/afc'
16+
17+
@Component
18+
export default class AfcCardBypass extends Mixins(StateMixin, AfcMixin) {
19+
get bypassState (): boolean {
20+
return this.afc?.bypass_state === true
21+
}
22+
}
23+
</script>

0 commit comments

Comments
 (0)