diff --git a/public/img/mmu/mmu_EMU.svg b/public/img/mmu/mmu_EMU.svg new file mode 100644 index 0000000000..d7eac41051 --- /dev/null +++ b/public/img/mmu/mmu_EMU.svg @@ -0,0 +1,2491 @@ + + + + + + + + + diff --git a/src/components/widgets/mmu/MmuCard.vue b/src/components/widgets/mmu/MmuCard.vue index df48652dcd..63f985bfaa 100644 --- a/src/components/widgets/mmu/MmuCard.vue +++ b/src/components/widgets/mmu/MmuCard.vue @@ -299,7 +299,7 @@ export default class MmuCard extends Mixins(StateMixin, MmuMixin) { } get showClogDetection (): boolean { - return !this.hasEncoder || !!this.$typedState.config.uiSettings.mmu.showClogDetection + return this.hasEncoder && this.$typedState.config.uiSettings.mmu.showClogDetection } get showTtgMap (): boolean { diff --git a/src/components/widgets/mmu/MmuSettings.vue b/src/components/widgets/mmu/MmuSettings.vue index b474d9fc2a..abc6807cb0 100644 --- a/src/components/widgets/mmu/MmuSettings.vue +++ b/src/components/widgets/mmu/MmuSettings.vue @@ -77,6 +77,17 @@ + + + + + + + {{ $t('app.mmu.setting.show_name') }} + + + + @@ -158,6 +169,18 @@ export default class MmuSettings extends Mixins(StateMixin, MmuMixin) { }) } + get showName (): boolean { + return this.$typedState.config.uiSettings.mmu.showName + } + + set showName (value: boolean) { + this.$typedDispatch('config/saveByPath', { + path: 'uiSettings.mmu.showName', + value, + server: true + }) + } + get showLogos (): boolean { return this.$typedState.config.uiSettings.mmu.showLogos } diff --git a/src/components/widgets/mmu/MmuUnit.vue b/src/components/widgets/mmu/MmuUnit.vue index c8464e49ca..bebcfa8f4c 100644 --- a/src/components/widgets/mmu/MmuUnit.vue +++ b/src/components/widgets/mmu/MmuUnit.vue @@ -239,8 +239,16 @@ @error="vendorLogo = 'HappyHare'" /> -
- {{ unitDisplayName }} +
+
+ {{ unitDisplayName }} +
+
+ {{ unitClimateInfo }} +
@@ -252,6 +260,7 @@ import BrowserMixin from '@/mixins/browser' import StateMixin from '@/mixins/state' import MmuMixin from '@/mixins/mmu' import type { MmuGateDetails } from '@/types' +import type { Sensor } from '@/store/printer/types' import MmuSpool from '@/components/widgets/mmu/MmuSpool.vue' import MmuGateStatus from '@/components/widgets/mmu/MmuGateStatus.vue' @@ -287,6 +296,33 @@ export default class MmuUnit extends Mixins(BrowserMixin, StateMixin, MmuMixin) return `#${this.unitIndex + 1} ${name}` } + get printerSensors (): Sensor[] { + return this.$typedGetters['printer/getSensors'] + } + + get unitClimateInfo (): string { + const unit = this.unitDetails(this.unitIndex) + + // Handle missing or quoted sensor name + const sensorName = unit.environmentSensor?.replace(/^"(.*)"$/, '$1') + if (!sensorName) return '' + + const sensor = this.printerSensors.find(s => s.key === sensorName) + if (!sensor) return '' + + const parts: string[] = [] + + if (sensor.temperature != null) { + parts.push(`${sensor.temperature.toFixed(0)}°C`) + } + + if (sensor.humidity != null) { + parts.push(`${sensor.humidity.toFixed(0)}%`) + } + + return parts.join(' / ') + } + get unitGateRange (): number[] { const unitDetails = this.unitDetails(this.unitIndex) return Array.from({ length: unitDetails.numGates }, (v, k) => k + unitDetails.firstGate) @@ -321,6 +357,10 @@ export default class MmuUnit extends Mixins(BrowserMixin, StateMixin, MmuMixin) return this.spoolWidth - 8 } + get showName (): boolean { + return this.$typedState.config.uiSettings.mmu.showName + } + get showLogos (): boolean { return this.$typedState.config.uiSettings.mmu.showLogos } @@ -364,16 +404,12 @@ export default class MmuUnit extends Mixins(BrowserMixin, StateMixin, MmuMixin) const firstGate = gate === 0 const lastGate = (gate === this.unitGateRange.length - 1 && !this.showBypass) || gate === this.TOOL_GATE_BYPASS const classes = ['gate-status-row'] - if (firstGate) classes.push('first-gate' + (this.isFirefox() ? '-firefox' : '')) - if (lastGate) classes.push('last-gate' + (this.isFirefox() ? '-firefox' : '')) + if (firstGate) classes.push('first-gate') + if (lastGate) classes.push('last-gate') classes.push(this.$vuetify.theme.dark ? 'gate-status-row-dark-theme' : 'gate-status-row-light-theme') return classes } - isFirefox (): boolean { - return navigator.userAgent.indexOf('Firefox') !== -1 - } - spoolClass (gate: number): string[] { const classes = [] if ((this.editGateMap && this.editGateSelected === gate) || (!this.editGateMap && this.gate === gate)) { @@ -469,14 +505,29 @@ export default class MmuUnit extends Mixins(BrowserMixin, StateMixin, MmuMixin) opacity: 0.7; } +.unit-info { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 100%; +} + .unit-name { display: flex; - align-items: center; + align-items: flex-end; font-size: 12px; white-space: nowrap; - margin-right: -12px; overflow: hidden; - padding: 0px 0px 4px 0px; + padding: 4px 0 0 0; +} + +.unit-climate { + font-size: 10px; + white-space: nowrap; + overflow: hidden; + text-align: right; + padding: 0px; + opacity: 0.8; } .gate-status-row-dark-theme { @@ -505,22 +556,6 @@ export default class MmuUnit extends Mixins(BrowserMixin, StateMixin, MmuMixin) border-radius: 8px 8px 10px 10px; } -.last-gate-firefox { - border-radius: 0 8px 10px 0px; - padding-right: 16px; -} - -.first-gate-firefox { - border-radius: 8px 0 0px 10px; - margin-left: -16px; - padding-left: 16px; - margin-right: 16px; -} - -.first-gate-firefox.last-gate-firefox { - border-radius: 8px 8px 10px 10px; -} - .clip-spool { position: relative; margin-top: 8px; diff --git a/src/locales/en.yaml b/src/locales/en.yaml index a272c46e1d..0751bd5feb 100644 --- a/src/locales/en.yaml +++ b/src/locales/en.yaml @@ -1121,6 +1121,7 @@ app: show_clog_detection: Show Clog Detection show_details: Show Filament Details show_ttg_map: Show TTG Map + show_name: Show Unit Name show_logos: Show Logos show_unavailable_spool_color: Show Unavailable Spools sensors: diff --git a/src/mixins/mmu.ts b/src/mixins/mmu.ts index 53f7948095..9083fabffc 100644 --- a/src/mixins/mmu.ts +++ b/src/mixins/mmu.ts @@ -71,7 +71,8 @@ export default class MmuMixin extends Vue { requireBowdenMove: mmuMachine?.[unitRef]?.require_bowden_move ?? true, filamentAlwaysGripped: mmuMachine?.[unitRef]?.filament_always_gripped ?? false, hasBypass: mmuMachine?.[unitRef]?.has_bypass ?? false, - multiGear: mmuMachine?.[unitRef]?.multi_gear ?? false + multiGear: mmuMachine?.[unitRef]?.multi_gear ?? false, + environmentSensor: mmuMachine?.[unitRef]?.environment_sensor ?? '' } } diff --git a/src/store/config/state.ts b/src/store/config/state.ts index 35c1bee8da..d938bfc6e4 100644 --- a/src/store/config/state.ts +++ b/src/store/config/state.ts @@ -152,6 +152,7 @@ export const defaultState = (): ConfigState => { showTtgMap: true, showDetails: true, largeFilamentStatus: false, + showName: true, showLogos: false, showUnavailableSpoolColor: false } diff --git a/src/store/config/types.ts b/src/store/config/types.ts index f0fa18945a..bb00b8dddf 100644 --- a/src/store/config/types.ts +++ b/src/store/config/types.ts @@ -65,6 +65,7 @@ export interface MmuConfig { showTtgMap: boolean; showDetails: boolean; largeFilamentStatus: boolean; + showName: boolean; showLogos: boolean; showUnavailableSpoolColor: boolean; } diff --git a/src/store/printer/types.ts b/src/store/printer/types.ts index 3fdb3ebe2e..083e10a850 100644 --- a/src/store/printer/types.ts +++ b/src/store/printer/types.ts @@ -726,6 +726,7 @@ type KlipperPrinterMmuMachineStateBaseType = { filament_always_gripped: boolean; has_bypass: boolean; multi_gear: boolean; + environment_sensor?: string; }; } diff --git a/src/types/mmu.ts b/src/types/mmu.ts index 3d8949978f..7d915b1725 100644 --- a/src/types/mmu.ts +++ b/src/types/mmu.ts @@ -31,4 +31,5 @@ export interface MmuUnitDetails { filamentAlwaysGripped: boolean hasBypass: boolean multiGear: boolean + environmentSensor: string }