From 5007646ff06b6fe4c4f1cb1820712df9f5210eda Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Tue, 19 Sep 2023 20:01:07 +0200 Subject: [PATCH 01/19] feat: add warning for outdated browsers (#1537) --- package-lock.json | 11 ++++++ package.json | 1 + src/locales/de.json | 4 ++ src/locales/en.json | 4 ++ src/store/gui/notifications/getters.ts | 52 +++++++++++++++++++++++++- src/store/variables.ts | 1 + 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 85359b3e2..ac924769e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "axios": "^0.27.0", "codemirror": "^6.0.1", "core-js": "^3.16.0", + "detect-browser": "^5.3.0", "echarts": "^5.2.2", "echarts-gl": "^2.0.8", "hls.js": "^1.3.3", @@ -4991,6 +4992,11 @@ "node": ">=0.4.0" } }, + "node_modules/detect-browser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz", + "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -14179,6 +14185,11 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "detect-browser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz", + "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==" + }, "dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", diff --git a/package.json b/package.json index 5e74e7920..346a98df3 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "axios": "^0.27.0", "codemirror": "^6.0.1", "core-js": "^3.16.0", + "detect-browser": "^5.3.0", "echarts": "^5.2.2", "echarts-gl": "^2.0.8", "hls.js": "^1.3.3", diff --git a/src/locales/de.json b/src/locales/de.json index 6d7d1b593..d4da31f9e 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1,6 +1,10 @@ { "App": { "Notifications": { + "BrowserWarnings": { + "Headline": "Veralteter Browser", + "Description": "Die verwendete {name} Version ({version}) ist veraltet und wird nicht vollständig unterstützt. Mainsail benötigt die Version {minVersion} oder höher." + }, "DependencyDescription": "Die momentane {name} Version unterstützt nicht alle Funktionen von Mainsail. Aktualisiere {name} mindestens auf Version {neededVersion}.", "DependencyName": "Abhängigkeit: {name}", "DismissAll": "Alles verwerfen", diff --git a/src/locales/en.json b/src/locales/en.json index 704471f03..9612e5638 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,6 +1,10 @@ { "App": { "Notifications": { + "BrowserWarnings": { + "Headline": "Outdated Browser", + "Description": "{name} is outdated and not fully supported. The current version is {version}, but Mainsail requires version {minVersion} or higher." + }, "DependencyDescription": "The current {name} version does not support all features of Mainsail. Update {name} to at least {neededVersion}.", "DependencyName": "Dependency: {name}", "DismissAll": "Dismiss all", diff --git a/src/store/gui/notifications/getters.ts b/src/store/gui/notifications/getters.ts index de456430d..a4072f186 100644 --- a/src/store/gui/notifications/getters.ts +++ b/src/store/gui/notifications/getters.ts @@ -5,6 +5,9 @@ import i18n from '@/plugins/i18n.js' import { RootStateDependency } from '@/store/types' import { sha256 } from 'js-sha256' import { PrinterStateKlipperConfigWarning } from '@/store/printer/types' +import { detect } from 'detect-browser' +import semver from 'semver' +import { minBrowserVersions } from '@/store/variables' export const getters: GetterTree = { getNotifications: (state, getters) => { @@ -22,12 +25,15 @@ export const getters: GetterTree = { // moonraker warnings notifications = notifications.concat(getters['getNotificationsMoonrakerWarnings']) - // moonraker failed compontents + // moonraker failed components notifications = notifications.concat(getters['getNotificationsMoonrakerFailedComponents']) // klipper warnings notifications = notifications.concat(getters['getNotificationsKlipperWarnings']) + // browser warnings + notifications = notifications.concat(getters['getNotificationsBrowserWarnings']) + const mapType = { normal: 2, high: 1, @@ -270,6 +276,50 @@ export const getters: GetterTree = { return notifications }, + getNotificationsBrowserWarnings: (state, getters, rootState) => { + const notifications: GuiNotificationStateEntry[] = [] + + const browser = detect() + const date = rootState.server.system_boot_at ?? new Date() + + // stop here, because no browser detected + if (browser === null) return notifications + + // output browser information to console + window.console.debug(`Browser: ${browser.name} ${browser.version}, OS: ${browser.os}`) + + // find browser requirement + const minBrowserVersion = minBrowserVersions.find( + (entry) => entry.name.toLowerCase() === browser.name.toLowerCase() + ) + + // stop here, because no browser requirement found + if (minBrowserVersion === undefined) return notifications + + if ( + semver.valid(browser.version) && + semver.valid(minBrowserVersion.version) && + semver.gt(minBrowserVersion.version, browser.version ?? '0.0.0') + ) { + notifications.push({ + id: `browserWarning/${minBrowserVersion.name}/${minBrowserVersion.version}`, + priority: 'critical', + title: i18n.t('App.Notifications.BrowserWarnings.Headline').toString(), + description: i18n + .t('App.Notifications.BrowserWarnings.Description', { + name: minBrowserVersion.name, + version: browser.version, + minVersion: minBrowserVersion.version, + }) + .toString(), + date, + dismissed: false, + } as GuiNotificationStateEntry) + } + + return notifications + }, + getDismiss: (state, getters, rootState) => { const currentTime = new Date() const systemBootAt = rootState.server.system_boot_at ?? new Date() diff --git a/src/store/variables.ts b/src/store/variables.ts index d2a847be4..df540bde7 100644 --- a/src/store/variables.ts +++ b/src/store/variables.ts @@ -3,6 +3,7 @@ export const defaultPrimaryColor = '#2196f3' export const minKlipperVersion = 'v0.11.0-97' export const minMoonrakerVersion = 'v0.8.0-38' +export const minBrowserVersions = [{ name: 'safari', version: '16.5.2' }] export const colorArray = ['#F44336', '#8e379d', '#03DAC5', '#3F51B5', '#ffde03', '#009688', '#E91E63'] From 9a2328ba69e6ec9eb3102b88d7261d5a73889a3c Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Tue, 19 Sep 2023 20:03:14 +0200 Subject: [PATCH 02/19] feat: automatic selection of the gcode offset save gcode (#1531) --- .../ToolheadControls/ZoffsetControl.vue | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/components/panels/ToolheadControls/ZoffsetControl.vue b/src/components/panels/ToolheadControls/ZoffsetControl.vue index 2eae278c1..52f974cae 100644 --- a/src/components/panels/ToolheadControls/ZoffsetControl.vue +++ b/src/components/panels/ToolheadControls/ZoffsetControl.vue @@ -27,11 +27,7 @@ {{ $t('Panels.ZoffsetPanel.Clear') }} {{ mdiContentSave }} {{ $t('Panels.ZoffsetPanel.Save') }} - - - - - - {{ mdiElectricSwitch }} - {{ $t('Panels.ZoffsetPanel.ToEndstop') }} - - - - - {{ mdiElevator }} - {{ $t('Panels.ZoffsetPanel.ToProbe') }} - - - - @@ -123,7 +94,7 @@ @@ -160,12 +131,9 @@ import Responsive from '@/components/ui/Responsive.vue' import { mdiBroom, - mdiElectricSwitch, - mdiElevator, mdiContentSave, mdiArrowCollapseDown, mdiInformation, - mdiMenuDown, mdiArrowExpandUp, mdiLayersOutline, } from '@mdi/js' @@ -174,16 +142,13 @@ import { }) export default class ZoffsetControl extends Mixins(BaseMixin) { mdiBroom = mdiBroom - mdiElectricSwitch = mdiElectricSwitch - mdiElevator = mdiElevator mdiContentSave = mdiContentSave mdiArrowCollapseDown = mdiArrowCollapseDown mdiInformation = mdiInformation - mdiMenuDown = mdiMenuDown mdiArrowExpandUp = mdiArrowExpandUp mdiLayersOutline = mdiLayersOutline - private saveOffsetDialog = false + saveOffsetDialog = false get homing_origin() { return this.$store.state.printer?.gcode_move?.homing_origin ?? [] @@ -198,13 +163,37 @@ export default class ZoffsetControl extends Mixins(BaseMixin) { } get homed_axis() { - return this.$store.state.printer.toolhead.homed_axes ?? '' + return this.$store.state.printer.toolhead?.homed_axes ?? '' } get helplist() { return this.$store.state.printer.helplist ?? [] } + get settings() { + return this.$store.state.printer.configfile?.settings ?? {} + } + + get kinematics() { + return this.settings.printer?.kinematics ?? 'cartesian' + } + + get stepper_name() { + if (this.kinematics === 'delta') return 'stepper_a' + + return 'stepper_z' + } + + get endstop_pin() { + const stepperConfig = this.settings[this.stepper_name] ?? {} + + return stepperConfig?.endstop_pin + } + + get isEndstopProbe() { + return this.endstop_pin.search('probe:z_virtual_endstop') !== -1 + } + get existZOffsetApplyProbe() { return this.helplist.findIndex((gcode: CommandHelp) => gcode.commandLow === 'z_offset_apply_probe') !== -1 } @@ -217,6 +206,17 @@ export default class ZoffsetControl extends Mixins(BaseMixin) { return this.$store.state.printer?.gcode_move?.homing_origin[2].toFixed(3) } + get showSaveButton() { + // hide button when offset is 0 + if (this.z_gcode_offset === 0) return false + + // show button when z endstop is probe and probe gcode exists + if (this.isEndstopProbe && this.existZOffsetApplyProbe) return true + + // show button when z endstop is endstop and endstop gcode exists + return !this.isEndstopProbe && this.existZOffsetApplyEndstop + } + sendBabyStepDown(offset: number): void { const gcode = `SET_GCODE_OFFSET Z_ADJUST=-${offset} ${this.homed_axis === 'xyz' ? 'MOVE=1' : ''}` this.$store.dispatch('server/addEvent', { message: gcode, type: 'command' }) @@ -236,12 +236,12 @@ export default class ZoffsetControl extends Mixins(BaseMixin) { } saveZOffset(): void { - if (this.existZOffsetApplyProbe && !this.existZOffsetApplyEndstop) { + if (this.isEndstopProbe && this.existZOffsetApplyProbe) { this.saveZOffsetToProbe() + return } - if (!this.existZOffsetApplyProbe && this.existZOffsetApplyEndstop) { - this.saveZOffsetToEndstop() - } + + this.saveZOffsetToEndstop() } saveZOffsetToEndstop(): void { From a0ad7e84ab454407d39010157b10e76eff3366c7 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Tue, 19 Sep 2023 20:04:22 +0200 Subject: [PATCH 03/19] feat: hide Moonraker power devices with a `_` as first char (#1545) --- src/components/TheTopCornerMenu.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/TheTopCornerMenu.vue b/src/components/TheTopCornerMenu.vue index 97241b626..0690a4439 100644 --- a/src/components/TheTopCornerMenu.vue +++ b/src/components/TheTopCornerMenu.vue @@ -238,7 +238,9 @@ export default class TheTopCornerMenu extends Mixins(BaseMixin) { } get powerDevices() { - return this.$store.getters['server/power/getDevices'] + const devices = this.$store.getters['server/power/getDevices'] ?? [] + + return devices.filter((device: ServerPowerStateDevice) => !device.device.startsWith('_')) } get service_states() { From 09c25266422ba295c5b2a397df81c7a3b9f0d6a4 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Tue, 19 Sep 2023 20:04:36 +0200 Subject: [PATCH 04/19] feat: add option to block autoscroll in console (#1519) --- src/components/panels/MiniconsolePanel.vue | 58 +++++++++++++++------- src/locales/en.json | 2 + src/pages/Console.vue | 58 +++++++++++++++------- src/store/gui/console/index.ts | 1 + src/store/gui/console/types.ts | 1 + 5 files changed, 82 insertions(+), 38 deletions(-) diff --git a/src/components/panels/MiniconsolePanel.vue b/src/components/panels/MiniconsolePanel.vue index 16a947f2d..93daad7a3 100644 --- a/src/components/panels/MiniconsolePanel.vue +++ b/src/components/panels/MiniconsolePanel.vue @@ -1,13 +1,3 @@ - - @@ -70,6 +77,14 @@ export default class TemperaturePanelList extends Mixins(BaseMixin) { return this.$store.state.printer?.heaters?.available_sensors ?? [] } + get available_monitors() { + return this.$store.state.printer?.heaters?.available_monitors ?? [] + } + + get monitors() { + return this.available_monitors.sort(this.sortObjectName) + } + get temperature_fans() { return this.available_sensors .filter((name: string) => name.startsWith('temperature_fan') && !name.startsWith('temperature_fan _')) @@ -84,6 +99,10 @@ export default class TemperaturePanelList extends Mixins(BaseMixin) { return this.$store.state.gui.view.tempchart.hideMcuHostSensors ?? false } + get hideMonitors(): boolean { + return this.$store.state.gui.view.tempchart.hideMonitors ?? false + } + get temperature_sensors() { return this.available_sensors .filter((fullName: string) => { diff --git a/src/components/panels/Temperature/TemperaturePanelListItem.vue b/src/components/panels/Temperature/TemperaturePanelListItem.vue index f2a4f56ec..fdb7a24ec 100644 --- a/src/components/panels/Temperature/TemperaturePanelListItem.vue +++ b/src/components/panels/Temperature/TemperaturePanelListItem.vue @@ -68,6 +68,7 @@ import { convertName } from '@/plugins/helpers' import { mdiFan, mdiFire, + mdiMemory, mdiPrinter3dNozzle, mdiPrinter3dNozzleAlert, mdiRadiator, @@ -131,6 +132,9 @@ export default class TemperaturePanelListItem extends Mixins(BaseMixin) { // show heater_generic icon if (this.objectName.startsWith('heater_generic')) return mdiFire + // show heater_generic icon + if (this.objectName.startsWith('tmc')) return mdiMemory + // show fan icon, if it is a fan if (this.isFan) return mdiFan @@ -138,7 +142,7 @@ export default class TemperaturePanelListItem extends Mixins(BaseMixin) { } get color() { - return this.$store.getters['printer/tempHistory/getDatasetColor'](this.objectName) ?? '' + return this.$store.getters['printer/tempHistory/getDatasetColor'](this.objectName) ?? '#FFFFFF' } get iconColor() { diff --git a/src/components/panels/Temperature/TemperaturePanelSettings.vue b/src/components/panels/Temperature/TemperaturePanelSettings.vue index c00f80b90..08cd33225 100644 --- a/src/components/panels/Temperature/TemperaturePanelSettings.vue +++ b/src/components/panels/Temperature/TemperaturePanelSettings.vue @@ -20,6 +20,13 @@ hide-details :label="$t('Panels.TemperaturePanel.HideMcuHostSensors')" /> + + + diff --git a/src/locales/en.json b/src/locales/en.json index 6e03f54bd..f66f3878d 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -684,6 +684,7 @@ }, "Headline": "Temperatures", "HideMcuHostSensors": "Hide Host/MCU Sensors", + "HideMonitors": "Hide Monitors", "Max": "max", "Min": "min", "Name": "Name", diff --git a/src/store/gui/index.ts b/src/store/gui/index.ts index c484e21da..186b6bfce 100644 --- a/src/store/gui/index.ts +++ b/src/store/gui/index.ts @@ -239,6 +239,7 @@ export const getDefaultState = (): GuiState => { boolTempchart: true, hiddenDataset: [], hideMcuHostSensors: false, + hideMonitors: false, autoscale: false, datasetSettings: {}, }, diff --git a/src/store/gui/types.ts b/src/store/gui/types.ts index 8eba4690b..5cc904fb7 100644 --- a/src/store/gui/types.ts +++ b/src/store/gui/types.ts @@ -165,6 +165,7 @@ export interface GuiState { boolTempchart: boolean hiddenDataset: string[] hideMcuHostSensors: boolean + hideMonitors: boolean autoscale: boolean datasetSettings: any } diff --git a/src/store/printer/actions.ts b/src/store/printer/actions.ts index 8a89be88e..a2af4be52 100644 --- a/src/store/printer/actions.ts +++ b/src/store/printer/actions.ts @@ -57,7 +57,12 @@ export const actions: ActionTree = { if (Object.keys(subscripts).length > 0) Vue.$socket.emit('printer.objects.subscribe', { objects: subscripts }, { action: 'printer/getInitData' }) - else Vue.$socket.emit('server.temperature_store', {}, { action: 'printer/tempHistory/init' }) + else + Vue.$socket.emit( + 'server.temperature_store', + { include_monitors: true }, + { action: 'printer/tempHistory/init' } + ) dispatch('socket/removeInitModule', 'printer/initSubscripts', { root: true }) }, @@ -70,7 +75,7 @@ export const actions: ActionTree = { dispatch('getData', payload) - Vue.$socket.emit('server.temperature_store', {}, { action: 'printer/tempHistory/init' }) + Vue.$socket.emit('server.temperature_store', { include_monitors: true }, { action: 'printer/tempHistory/init' }) setTimeout(() => { dispatch('initExtruderCanExtrude') diff --git a/src/store/printer/getters.ts b/src/store/printer/getters.ts index 38253d0f6..6186c9fa6 100644 --- a/src/store/printer/getters.ts +++ b/src/store/printer/getters.ts @@ -372,6 +372,10 @@ export const getters: GetterTree = { return state.heaters?.available_sensors ?? [] }, + getAvailableMonitors: (state) => { + return state.heaters?.available_monitors ?? [] + }, + getFilamentSensors: (state) => { const sensorObjectNames = ['filament_switch_sensor', 'filament_motion_sensor'] const sensors: PrinterStateFilamentSensors[] = [] diff --git a/src/store/printer/tempHistory/actions.ts b/src/store/printer/tempHistory/actions.ts index 6b09d0d1c..f4bf566e3 100644 --- a/src/store/printer/tempHistory/actions.ts +++ b/src/store/printer/tempHistory/actions.ts @@ -28,6 +28,7 @@ export const actions: ActionTree = { const now = new Date() const allHeaters = rootGetters['printer/getAvailableHeaters'] ?? [] const allSensors = rootGetters['printer/getAvailableSensors'] ?? [] + const allMonitors = rootGetters['printer/getAvailableMonitors'] ?? [] const maxHistory = rootGetters['printer/tempHistory/getTemperatureStoreSize'] if (payload !== undefined) { @@ -44,7 +45,7 @@ export const actions: ActionTree = { } // break if sensor doesn't exist anymore or start with a _ - if (!allSensors.includes(key) || nameOnly.startsWith('_')) { + if (!(allSensors.includes(key) || allMonitors.includes(key)) || nameOnly.startsWith('_')) { delete payload[key] return } @@ -54,7 +55,7 @@ export const actions: ActionTree = { if (datasetKey + 's' in datasetValues) { const length = maxHistory - datasetValues[datasetKey + 's'].length datasetValues[datasetKey + 's'] = [ - ...Array.from({ length }, () => 0), + ...Array.from({ length }, () => null), ...datasetValues[datasetKey + 's'], ] } @@ -64,7 +65,8 @@ export const actions: ActionTree = { }) // add missing heaters/sensors - allSensors.forEach((key: string) => { + const allEntries = allSensors.concat(allMonitors) + allEntries.forEach((key: string) => { // break if sensor is already in the cache if (key in payload) return @@ -85,15 +87,15 @@ export const actions: ActionTree = { powers?: number[] speeds?: number[] } = { - temperatures: Array(maxHistory).fill(0), + temperatures: Array(maxHistory).fill(null), } if (allHeaters.includes(key)) { - addValues.targets = Array(maxHistory).fill(0) - addValues.powers = Array(maxHistory).fill(0) + addValues.targets = Array(maxHistory).fill(null) + addValues.powers = Array(maxHistory).fill(null) } else if (['temperature_fan'].includes(sensorType)) { - addValues.targets = Array(maxHistory).fill(0) - addValues.speeds = Array(maxHistory).fill(0) + addValues.targets = Array(maxHistory).fill(null) + addValues.speeds = Array(maxHistory).fill(null) } importData[key] = { ...addValues } @@ -239,7 +241,11 @@ export const actions: ActionTree = { window.console.debug('update Source', t0-state.timeLastUpdate) }*/ - if (rootState?.printer?.heaters?.available_sensors?.length) { + const allSensors = rootGetters['printer/getAvailableSensors'] ?? [] + const allMonitors = rootGetters['printer/getAvailableMonitors'] ?? [] + const items = allSensors.concat(allMonitors) + + if (items.length) { const now = new Date() if (state.source.length) { @@ -255,14 +261,15 @@ export const actions: ActionTree = { date: now, } - rootState.printer.heaters.available_sensors.forEach((name: string) => { + items.forEach((name: string) => { if (!(rootState.printer && name in rootState.printer)) return const printerObject = { ...rootState.printer[name] } datasetTypes.forEach((attrKey) => { if (!(attrKey in printerObject)) return - let value = Math.round(printerObject[attrKey] * 10) / 10 + let value = printerObject[attrKey] + if (value !== null) value = Math.round(printerObject[attrKey] * 10) / 10 if (datasetTypesInPercents.includes(attrKey)) value = Math.round(printerObject[attrKey] * 1000) / 1000 diff --git a/src/store/printer/tempHistory/getters.ts b/src/store/printer/tempHistory/getters.ts index 53ed0d318..d9b591efd 100644 --- a/src/store/printer/tempHistory/getters.ts +++ b/src/store/printer/tempHistory/getters.ts @@ -145,6 +145,21 @@ export const getters: GetterTree = { }) } + // hide Monitors, if the option is set to true + const hideMonitors = rootState.gui?.view?.tempchart?.hideMonitors ?? false + if (hideMonitors) { + const monitors = rootState.printer?.heaters?.available_monitors ?? [] + + Object.keys(selected) + .filter((seriesName) => { + const datasetName = seriesName.slice(0, seriesName.lastIndexOf('-')) + return monitors.includes(datasetName) + }) + .forEach((seriesName) => { + selected[seriesName] = false + }) + } + return selected }, diff --git a/src/store/variables.ts b/src/store/variables.ts index df540bde7..2ff9a43cb 100644 --- a/src/store/variables.ts +++ b/src/store/variables.ts @@ -1,8 +1,8 @@ export const defaultLogoColor = '#D41216' export const defaultPrimaryColor = '#2196f3' -export const minKlipperVersion = 'v0.11.0-97' -export const minMoonrakerVersion = 'v0.8.0-38' +export const minKlipperVersion = 'v0.11.0-257' +export const minMoonrakerVersion = 'v0.8.0-137' export const minBrowserVersions = [{ name: 'safari', version: '16.5.2' }] export const colorArray = ['#F44336', '#8e379d', '#03DAC5', '#3F51B5', '#ffde03', '#009688', '#E91E63']