From fa61d4ef9297426a404dd845a1a4d5e4525c43dc Mon Sep 17 00:00:00 2001 From: meteyou Date: Sat, 4 May 2024 07:00:15 +0000 Subject: [PATCH 01/45] docs(changelog): update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d098c028d..cd605a3d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ # Changelog All notable changes to Mainsail will be documented in this file. +## [2.11.2](https://github.com/mainsail-crew/mainsail/releases/tag/v2.11.2) - 2024-05-04 +### Bug Fixes and Improvements + +- **maintenance**: Fix overdue check from printtime based entries ([#1871](https://github.com/mainsail-crew/mainsail/pull/1871)) +- **spoolman**: Fix search for spool-id ([#1872](https://github.com/mainsail-crew/mainsail/pull/1872)) +- Calc multiplicator for set_pin gcode ([#1870](https://github.com/mainsail-crew/mainsail/pull/1870)) + ## [2.11.1](https://github.com/mainsail-crew/mainsail/releases/tag/v2.11.1) - 2024-05-01 ### Bug Fixes and Improvements From 57e3998a95f38f27ec638959ce9f0be8a8380eb3 Mon Sep 17 00:00:00 2001 From: evgarthub Date: Mon, 20 May 2024 19:30:44 +0300 Subject: [PATCH 02/45] locale(uk): update ukrainian locale (#1885) --- src/locales/uk.json | 95 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 75 insertions(+), 20 deletions(-) diff --git a/src/locales/uk.json b/src/locales/uk.json index 684818cc9..a42f3ceec 100644 --- a/src/locales/uk.json +++ b/src/locales/uk.json @@ -13,8 +13,11 @@ "DeprecatedOptionHeadline": "Застаріла опція Klipper", "DeprecatedValue": "Значення '{value}' опції '{option}' у секції '{section}' застаріла і буде видалена у майбутньому випуску.", "DeprecatedValueHeadline": "Застаріле значення Klipper", + "KlipperRuntimeWarning": "Попередження під час роботи Klipper", "KlipperWarning": "Попередження Klipper" }, + "MaintenanceReminder": "Нагадування про технічне обслуговування", + "MaintenanceReminderText": "Термін технічного обслуговування \"{name}\".", "MoonrakerWarnings": { "MoonrakerComponent": "Moonraker: {component}", "MoonrakerFailedComponentDescription": "Помилка була виявлена під час завантаження компонента Moonraker '{component}'. Будь ласка, перевірте файл журналу та виправте проблему.", @@ -28,7 +31,11 @@ "NextReboot": "наступне перезавантаження", "NoNotification": "Нема Повідомлень", "Notifications": "Повідомлення", - "Remind": "Нагадування:" + "OneDayShort": "1д", + "OneHourShort": "1год", + "OneWeekShort": "1тиж", + "Remind": "Нагадування:", + "ShowDetails": "показати деталі" }, "NumberInput": { "GreaterOrEqualError": "Повинен бути більшим або рівним, ніж {min}!", @@ -123,14 +130,14 @@ } }, "BedScrews": { - "Abort": "перервати", - "Accept": "прийняти", - "Adjusted": "відрегульований", - "Description": "Натисніть ADJUSTED, якщо поточний гвинт було відрегульовано. Натисніть ACCEPT, щоб продовжити без коригування.", + "Abort": "Перервати", + "Accept": "Прийняти", + "Adjusted": "Відкореговано", + "Description": "Натисніть ВІДКОРЕГОВАНО, якщо поточний гвинт було відрегульовано. Натисніть ПРИЙНЯТИ, щоб продовжити без коригування.", "Headline": "Гвинти столу", "ScrewAccepted": "Готові гвинти", - "ScrewIndex": "Індекс гвинта", - "ScrewName": "Ім'я гвинта", + "ScrewIndex": "Номер гвинта", + "ScrewName": "Назва гвинта", "ScrewOutput": "{current} з {max}" }, "ConnectionDialog": { @@ -139,7 +146,7 @@ "Connecting": "Підключення до {host}", "Failed": "Підключення не вдалося", "Initializing": "Ініціалізація", - "TryAgain": "спробуйте ще раз" + "TryAgain": "Спробуйте ще раз" }, "Console": { "CommandList": "Список команд", @@ -149,6 +156,12 @@ "SendCode": "Надіслати код...", "SetupConsole": "Консоль налаштування" }, + "CoolDownDialog": { + "AreYouSure": "Ти впевнений?", + "CoolDown": "Охолодження", + "No": "Ні", + "Yes": "Так" + }, "DevicesDialog": { "CanBusInfo": "Можна виявити лише непризначені вузли. Рекомендується мати лише один непризначений пристрій, підключений до шини can, щоб уникнути проблем зі зв’язком. Щоб отримати детальнішу інформацію, натисніть на посилання:", "ClickRefresh": "Натисніть кнопку оновити, щоб знайти пристрої.", @@ -160,7 +173,7 @@ "NoDeviceFound": "Пристрій не знайдено. Перевірте підключення та натисніть кнопку оновити.", "PathByHardware": "Фізичний шлях", "PathById": "Шлях за ID", - "Refresh": "оновити", + "Refresh": "Оновити", "Resolutions": "Резолюції" }, "Dialogs": { @@ -169,7 +182,7 @@ "DoYouWantToStartFilename": "Ви хочете роздрукувати {filename}?", "DoYouWantToStartFilenameFilament": "Хочете почати друк {filename} з наявного філаменту?", "Headline": "Почати друк", - "Print": "друк", + "Print": "Друк", "Timelapse": "Таймлапс" } }, @@ -316,7 +329,7 @@ "InvalidNameAlreadyExists": "Ім'я профілю вже існує, будь ласка, виберіть інше ім'я профілю.", "InvalidNameAscii": "Ім'я недійсне. Дозволяється лише символи ascii.", "InvalidNameEmpty": "Введення не повинно бути порожнім!", - "InvalidNameReserved": "Profile 'default' зарезервовано, будь ласка, виберіть інше ім'я профілю.", + "InvalidNameReserved": "Профіль 'default' зарезервовано, будь ласка, виберіть інше ім'я профілю.", "Mesh": "Сітка", "Name": "Ім'я", "NoBedMeshHasBeenLoadedYet": "Жодна сітка для ліжка ще не була завантажена.", @@ -334,6 +347,8 @@ "Wireframe": "Каркас" }, "History": { + "AddANote": "Додати примітку", + "AddMaintenance": "Додати обслуговування", "AddNote": "Додати коментар", "AddToQueueSuccessful": "Файл {filename} додано до черги.", "AllJobs": "ВСІ", @@ -341,16 +356,26 @@ "Cancel": "Скасувати", "Chart": "Графік", "CreateNote": "Створити Примітку", + "DateBasedReminder": "Дата", + "DateBasedReminderDescription": "Це нагадування базується на даті.", + "Days": "днів", "Delete": "Видалити", "DeleteSelectedQuestion": "Ви дійсно хочете видалити {count} вибране завдання?", "DeleteSingleJobQuestion": "Ви справді хочете видалити завдання?", "Details": "Деталі", + "EditMaintenance": "Редагувати технічне обслуговування", "EditNote": "Редагувати Примітку", "Empty": "порожньо", "EndTime": "Час Закінчення", + "EntryCreatedAt": "Створено {date}.", + "EntryNextPerform": "Наступне виконання:", + "EntryPerformedAt": "Виконано {date}.", + "EntrySince": "Використовується з:", "EstimatedFilament": "Орієнтовна довжина прутка", "EstimatedFilamentWeight": "Орієнтовна вага прутка", "EstimatedTime": "Орієнтовний Час", + "FilamentBasedReminder": "Філамент", + "FilamentBasedReminderDescription": "Це нагадування базується на використанні філаменту.", "FilamentCalc": "Калькулятор Прутка", "FilamentUsage": "Використання Прутка", "FilamentUsed": "Використано Прутка", @@ -361,18 +386,36 @@ "FirstLayerHeight": "Висота першого шару", "HistoryFilamentUsage": "Пруток", "HistoryPrinttimeAVG": "Друк", + "Hours": "годин", + "InvalidNameEmpty": "Недійсне ім'я. Поле не повинно бути пустим!", "JobDetails": "Деталі Завдань", "Jobs": "Завдання", "LastModified": "Дата Створення", "LayerHeight": "Висота Шару", "LoadCompleteHistory": "Завантажити повну історію", "LongestPrinttime": "Найдовший Час Друку", + "Maintenance": "Технічне обслуговування", + "MaintenanceEntries": "Записи технічного обслуговування", + "Meter": "метр", + "Name": "Назва", + "NoReminder": "Нагадування відсутні", "Note": "Примітка", "ObjectHeight": "Висота Об'єкта", + + "OneTime": "Одноразовий", + "Perform": "виконати", + "Performed": "виконано", + "PerformedAndReschedule": "виконано та переплановано", + "PerformMaintenance": "Виконати технічне обслуговування", "PrintDuration": "Час Друку", "PrintHistory": "Історія друку", + "PrintJobs": "Завдання друку", "PrintTime": "Час Друку", "PrinttimeAvg": "Час Друку - Ø", + "PrinttimeBasedReminder": "Час друку", + "PrinttimeBasedReminderDescription": "Це нагадування базується на часу друку.", + "Reminder": "Нагадування", + "Repeat": "Повторити", "Reprint": "Передрукувати", "Save": "зберегти", "Search": "пошук", @@ -589,7 +632,9 @@ "KlippyStatePanel": { "CheckKlippyAndUdsAddress": "Будь ласка, перевірте, чи працює служба Klipper і чи правильно налаштовано klippy_uds_address у файлі moonraker.conf.", "FirmwareRestart": "Перезапуск Прошивки", + "KlipperLog": "Журнал Klipper", "MoonrakerCannotConnect": "Moonraker не може підключитися до Klipper!", + "MoonrakerLog": "Журнал Moonraker", "PowerOn": "Увімкнено", "PrinterSwitchedOff": "Принтер вимкнений", "PrinterSwitchedOffDescription": "Принтер наразі вимкнено, і Klipper не може підключитися. Щоб увімкнути принтер, натисніть кнопку нижче:", @@ -608,7 +653,7 @@ }, "MacrosPanel": { "Headline": "Макрос", - "Send": "відправити" + "Send": "Відправити" }, "MiniconsolePanel": { "Autoscroll": "Автопрокручування", @@ -620,9 +665,9 @@ }, "MinSettingsPanel": { "IncludeMainsailCfg": "Переконайтеся, що ви включили mainsail.cfg у свій файл printer.cfg.", - "IsNotDefinedInConfig": "не визначається в конфігурації.", + "IsNotDefinedInConfig": "Відсутній імпорт в конфігурації.", "MissingConfiguration": "Відсутня конфігурація", - "MoreInformation": "більше інформації" + "MoreInformation": "Більше інформації" }, "MiscellaneousPanel": { "Headline": "Різне", @@ -801,11 +846,11 @@ "Webcam": "Веб-камера" }, "ScrewsTiltAdjust": { - "Accept": "прийняти", - "Base": "Основа", - "ErrorText": "Щось пішло не так під час процесу взяття проби.", - "Headline": "Гвинти регулювання нахилу", - "Retry": "повторити спробу" + "Accept": "Прийняти", + "Base": "Базовий", + "ErrorText": "Щось пішло не так під час взяття проби.", + "Headline": "Регулювання нахилу взяттям проби", + "Retry": "Повторити спробу" }, "SelectPrinterDialog": { "AddPrinter": "Додати Принтер", @@ -819,6 +864,8 @@ "HostnameInvalid": "Недійсне ім'я хоста/IP", "HostnameIp": "Ім'я хоста/IP", "HostnameRequired": "Необхідне ім'я хоста", + "Name": "Назва", + "Path": "Шлях", "Port": "Порт", "PortRequired": "Потрібен порт", "RememberToAdd": "Будь ласка, не забудьте додати '{cors}' у moonraker.conf всередині 'cors_domains'.", @@ -935,7 +982,8 @@ "DbConsoleHistory": "Історія Консолі", "DbHistoryJobs": "Історія завдань", "DbHistoryTotals": "Загальна Історія", - "DBNavigation": "Навігація", + "DbMaintenance": "Технічне обслуговування", + "DbNavigation": "Навігація", "DbTimelapseSettings": "Налаштування Таймлапсу", "DbView": "Налаштування перегляду", "EstimateValues": { @@ -1052,6 +1100,9 @@ "AddPrinter": "Додати принтер", "EditPrinter": "Редагувати принтер", "Hostname": "Ім'я хоста/ІР-адреса", + "Name": "Назва", + "NameDescription": "Це ім’я не відображатиметься в графічному інтерфейсі і використовуватиметься лише для перенаправлення.", + "Path": "Шлях", "Port": "Порт", "RemotePrinters": "Принтери", "UpdatePrinter": "Оновити принтер", @@ -1138,6 +1189,8 @@ "BoolBigThumbnailDescription": "Відобразити велику мініатюру на панелі статусу під час друку.", "BoolHideUploadAndPrintButton": "Приховати кнопку завантаження та друк", "BoolHideUploadAndPrintButtonDescription": "Показати або приховати кнопку «Завантажити та друканути» у верхній панелі.", + "ConfirmOnCoolDown": "Вимагати підтвердження для Охолодження", + "ConfirmOnCoolDownDescription": "Показати діалогове вікно підтвердження для Охолодження", "ConfirmOnEmergencyStop": "Вимагати підтвердження на АВАРІЙНУ ЗУПИНКУ", "ConfirmOnEmergencyStopDescription": "Показати діалогове вікно підтвердження АВАРІЙНОЇ ЗУПИНКИ", "ConfirmOnPowerDeviceChange": "Вимагати підтвердження змін живлення пристрою", @@ -1172,6 +1225,8 @@ "PowerDeviceName": "Пристрій живлення принтера", "PowerDeviceNameDescription": "Виберіть, який пристрій живлення Moonraker слід використовувати для живлення принтера.", "Primary": "Основний Колір", + "ProgressAsFavicon": "Показати прогрес у вкладці браузера", + "ProgressAsFaviconDescription": "Змініть іконку логотипу Mainsail на іконку прогресу.", "ScrewsTiltAdjustDialog": "Вікно налаштування гвинтів нахилу стола", "ScrewsTiltAdjustDialogDescription": "Відобразити допоміжне вікно для SCREWS_TILT_CALCULATE.", "TempchartHeight": "Висота графіку температур", From b91338ddb47d8185145c0a97eda6b0f6f7f705b6 Mon Sep 17 00:00:00 2001 From: Samuel Wang Date: Mon, 20 May 2024 09:31:13 -0700 Subject: [PATCH 03/45] locale(zh): update chinese locale (#1877) --- src/locales/zh.json | 76 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/src/locales/zh.json b/src/locales/zh.json index f33d907d1..9ff1fadbf 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -13,8 +13,11 @@ "DeprecatedOptionHeadline": "Klipper选项已被弃用", "DeprecatedValue": "在'{section}'标签中的'{option}'选项的参数'{value}'已被弃用,将在未来的版本中删除。", "DeprecatedValueHeadline": "Klipper参数已被弃用", + "KlipperRuntimeWarning": "Klipper运行时警告", "KlipperWarning": "Klipper警告" }, + "MaintenanceReminder": "保养提醒", + "MaintenanceReminderText": "\"{name}\" 已到保养期。", "MoonrakerWarnings": { "MoonrakerComponent": "Moonraker:{component}", "MoonrakerFailedComponentDescription": "加载moonraker组件'{component}'时发生错误。请检查日志文件并修复此问题。", @@ -28,7 +31,11 @@ "NextReboot": "下次重启", "NoNotification": "没有可用的通知", "Notifications": "通知", - "Remind": "提醒:" + "OneDayShort": "1天", + "OneHourShort": "1小时", + "OneWeekShort": "1周", + "Remind": "提醒:", + "ShowDetails": "显示详情" }, "NumberInput": { "GreaterOrEqualError": "必须大于或等于{min}!", @@ -149,6 +156,12 @@ "SendCode": "输入要执行的代码...", "SetupConsole": "设置控制台" }, + "CoolDownDialog": { + "AreYouSure": "是否确定?", + "CoolDown": "降温", + "No": "取消", + "Yes": "确定" + }, "DevicesDialog": { "CanBusInfo": "只有未分配的节点才能被检测到。建议仅连接一个未分配的设备到CAN总线,以避免通信问题。更多详情,请点击链接:", "ClickRefresh": "点击刷新按钮以搜索设备。", @@ -334,23 +347,35 @@ "Wireframe": "线框" }, "History": { - "AddNote": "添加便条", + "AddANote": "添加一条备注", + "AddMaintenance": "添加保养提醒", + "AddNote": "添加备注", "AddToQueueSuccessful": "文件{filename}已添加到队列。", "AllJobs": "全部", "AvgPrinttime": "平均打印时长", "Cancel": "取消", "Chart": "图表", - "CreateNote": "新建便条", + "CreateNote": "新建备注", + "DateBasedReminder": "日期", + "DateBasedReminderDescription": "本提醒基于日期。", + "Days": "天", "Delete": "删除", "DeleteSelectedQuestion": "你是否要删除已选中的{count}个任务?", "DeleteSingleJobQuestion": "你是否要删除这个任务?", "Details": "详情", - "EditNote": "编辑便条", + "EditMaintenance": "编辑保养提醒", + "EditNote": "编辑备注", "Empty": "没有内容", "EndTime": "结束时间", + "EntryCreatedAt": "创建于 {date}。", + "EntryNextPerform": "下次执行:", + "EntryPerformedAt": "已于 {date} 执行。", + "EntrySince": "启用时间:", "EstimatedFilament": "预估耗材用量", "EstimatedFilamentWeight": "预估耗材重量", "EstimatedTime": "预估打印时长", + "FilamentBasedReminder": "耗材", + "FilamentBasedReminderDescription": "本提醒基于耗材使用量。", "FilamentCalc": "耗材预估长度", "FilamentUsage": "耗材用量", "FilamentUsed": "实际耗材消耗量", @@ -361,18 +386,35 @@ "FirstLayerHeight": "首层高度", "HistoryFilamentUsage": "耗材用量", "HistoryPrinttimeAVG": "打印时长", + "Hours": "小时", + "InvalidNameEmpty": "无效名称。名称不能为空!", "JobDetails": "任务详情", "Jobs": "每页显示任务数", "LastModified": "修改日期", "LayerHeight": "层高", "LoadCompleteHistory": "加载完整历史记录", "LongestPrinttime": "最长打印时长", - "Note": "便条", + "Maintenance": "保养", + "MaintenanceEntries": "保养", + "Meter": "米", + "Name": "名称", + "NoReminder": "不提醒", + "Note": "备注", "ObjectHeight": "物体高度", + "OneTime": "一次性", + "Perform": "执行", + "Performed": "已执行", + "PerformedAndReschedule": "已执行并重新安排提醒", + "PerformMaintenance": "执行保养", "PrintDuration": "打印耗时", "PrintHistory": "打印历史", + "PrintJobs": "打印任务", "PrintTime": "打印持续时间", "PrinttimeAvg": "平均打印时长", + "PrinttimeBasedReminder": "打印时长", + "PrinttimeBasedReminderDescription": "本提醒基于打印时长", + "Reminder": "提醒", + "Repeat": "重复", "Reprint": "重新打印", "Save": "保存", "Search": "搜索", @@ -499,7 +541,7 @@ }, "UpdatePanel": { "Abort": "中止", - "AreYouSure": "你确定吗?", + "AreYouSure": "是否确定?", "CheckForUpdates": "检查更新", "Close": "关闭", "CommitHistory": "查看提交记录", @@ -589,7 +631,9 @@ "KlippyStatePanel": { "CheckKlippyAndUdsAddress": "请检查Klipper服务是否启动。", "FirmwareRestart": "重启Klipper固件", + "KlipperLog": "Klipper 日志", "MoonrakerCannotConnect": "Moonraker无法连接到Klipper !", + "MoonrakerLog": "Moonraker 日志", "PowerOn": "开启电源", "PrinterSwitchedOff": "打印机电源已关闭", "PrinterSwitchedOffDescription": "打印机电源已关闭,Klipper无法连接。点击下方按钮开启打印机电源:", @@ -738,8 +782,8 @@ "Presets": "预设", "SetupTemperatures": "配置温度", "ShowChart": "显示图表", - "ShowNameInChart": "在图表中显示{name} ", - "ShowNameInList": "在列表中显示{name} ", + "ShowNameInChart": "在图表中显示{name}", + "ShowNameInList": "在列表中显示{name}", "State": "状态", "Target": "目标", "TemperaturesInChart": "温度 [°C]", @@ -784,8 +828,8 @@ "PowerDeviceChangeDialog": { "AreYouSure": "是否确定?", "No": "取消", - "TurnDeviceOff": "关闭{device} ", - "TurnDeviceOn": "打开{device} ", + "TurnDeviceOff": "关闭{device}", + "TurnDeviceOn": "打开{device}", "Yes": "确定" }, "Router": { @@ -819,6 +863,8 @@ "HostnameInvalid": "不可用的主机名称/IP地址", "HostnameIp": "主机名称/IP地址", "HostnameRequired": "需要配置主机名称/IP地址", + "Name": "名称", + "Path": "路径", "Port": "Moonraker服务端口号", "PortRequired": "需要配置Moonraker服务端口号", "RememberToAdd": "请在moonraker.conf的'cors_domains'标签中添加'{cors}'", @@ -935,7 +981,8 @@ "DbConsoleHistory": "控制台历史记录", "DbHistoryJobs": "历史任务", "DbHistoryTotals": "历史总计", - "DBNavigation": "侧边栏", + "DbMaintenance": "保养", + "DbNavigation": "侧边栏", "DbTimelapseSettings": "延时摄影设置", "DbView": "视图设置", "EstimateValues": { @@ -1052,6 +1099,9 @@ "AddPrinter": "添加打印机", "EditPrinter": "编辑打印机", "Hostname": "主机名称/IP地址", + "Name": "名称", + "NameDescription": "此名称仅用于重定向,并不会在GUI上显示。", + "Path": "路径", "Port": "Moonraker服务端口", "RemotePrinters": "打印机", "UpdatePrinter": "更新打印机", @@ -1138,6 +1188,8 @@ "BoolBigThumbnailDescription": "打印时在状态框中显示大缩略图", "BoolHideUploadAndPrintButton": "隐藏\"上传并打印\"按钮", "BoolHideUploadAndPrintButtonDescription": "在顶栏显示或者隐藏\"上传并打印\"按钮", + "ConfirmOnCoolDown": "冷却需要确认", + "ConfirmOnCoolDownDescription": "在按下\"冷却\"时显示确认对话框", "ConfirmOnEmergencyStop": "紧急停止需要确认", "ConfirmOnEmergencyStopDescription": "在按下\"紧急停止\"时显示确认对话框", "ConfirmOnPowerDeviceChange": "控制设备电源时需要确认", @@ -1172,6 +1224,8 @@ "PowerDeviceName": "打印机电源设备", "PowerDeviceNameDescription": "请选择可以控制打印机供电的Moonraker电源设备。", "Primary": "高亮颜色", + "ProgressAsFavicon": "显示进度到浏览器标签", + "ProgressAsFaviconDescription": "将浏览器标签的Mainsail图标更改为进度圆圈。", "ScrewsTiltAdjustDialog": "螺丝倾斜调整对话框", "ScrewsTiltAdjustDialogDescription": "显示SCREWS_TILT_CALCULATE辅助对话框。", "TempchartHeight": "温度图表高度", From 5cfef22fa9ecbadda76575b60f11b3cc4eaaf919 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Mon, 20 May 2024 19:26:29 +0200 Subject: [PATCH 04/45] locale(en): add missing english locale (#1890) --- src/locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 7dc36a197..fdede52e2 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1176,9 +1176,9 @@ "VariableFps": "Variable FPS", "VariableFpsDescription": "If enabled, the framerate of the output video will be calculated based on target length", "VariableFpsMax": "Variable FPS max", - "VariableFpsMaxDescription": "", + "VariableFpsMaxDescription": "Maximum variable FPS value", "VariableFpsMin": "Variable FPS min", - "VariableFpsMinDescription": "" + "VariableFpsMinDescription": "Minimum variable FPS value" }, "UiSettingsTab": { "BedScrewsDialog": "Bed Screws Dialog", From 8bccd7e73e256c72ec88a6457ce3bae2cf9fc35e Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 25 May 2024 20:51:46 +0200 Subject: [PATCH 05/45] feat(history): add support for Moonraker sensor history_fields (#1884) --- .../dialogs/HistoryDetailsDialog.vue | 170 ------------- .../dialogs/HistoryListPanelDetailsDialog.vue | 16 ++ .../panels/History/HistoryListEntryJob.vue | 5 + .../panels/History/HistoryListRow.vue | 226 ------------------ .../panels/History/HistoryListRowCell.vue | 61 ----- src/components/panels/HistoryListPanel.vue | 89 ++++++- src/store/server/history/types.ts | 9 + src/store/server/index.ts | 2 + src/store/server/sensor/actions.ts | 26 ++ src/store/server/sensor/getters.ts | 5 + src/store/server/sensor/index.ts | 23 ++ src/store/server/sensor/mutations.ts | 18 ++ src/store/server/sensor/types.ts | 14 ++ src/store/socket/actions.ts | 4 + src/store/variables.ts | 1 + 15 files changed, 199 insertions(+), 470 deletions(-) delete mode 100644 src/components/dialogs/HistoryDetailsDialog.vue delete mode 100644 src/components/panels/History/HistoryListRow.vue delete mode 100644 src/components/panels/History/HistoryListRowCell.vue create mode 100644 src/store/server/sensor/actions.ts create mode 100644 src/store/server/sensor/getters.ts create mode 100644 src/store/server/sensor/index.ts create mode 100644 src/store/server/sensor/mutations.ts create mode 100644 src/store/server/sensor/types.ts diff --git a/src/components/dialogs/HistoryDetailsDialog.vue b/src/components/dialogs/HistoryDetailsDialog.vue deleted file mode 100644 index dfa3c2c34..000000000 --- a/src/components/dialogs/HistoryDetailsDialog.vue +++ /dev/null @@ -1,170 +0,0 @@ - - - diff --git a/src/components/dialogs/HistoryListPanelDetailsDialog.vue b/src/components/dialogs/HistoryListPanelDetailsDialog.vue index 16f7f33f8..8236db9c8 100644 --- a/src/components/dialogs/HistoryListPanelDetailsDialog.vue +++ b/src/components/dialogs/HistoryListPanelDetailsDialog.vue @@ -106,6 +106,22 @@ export default class HistoryListPanelDetailsDialog extends Mixins(BaseMixin) { }, ] + if ('auxiliary_data' in this.job) { + this.job.auxiliary_data?.forEach((data) => { + let value = data.value.toString() + if (!Array.isArray(data.value)) { + value = `${Math.round(data.value * 1000) / 1000} ${data.units}` + } + if (value === '') value = '--' + + entries.push({ + name: data.description, + value, + exists: true, + }) + }) + } + return entries.filter((entry) => entry.exists) } diff --git a/src/components/panels/History/HistoryListEntryJob.vue b/src/components/panels/History/HistoryListEntryJob.vue index 29cd970b6..aa8da85d9 100644 --- a/src/components/panels/History/HistoryListEntryJob.vue +++ b/src/components/panels/History/HistoryListEntryJob.vue @@ -300,6 +300,11 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { //@ts-ignore let value = col.value in item ? item[col.value] : null if (value === null) value = col.value in item.metadata ? item.metadata[col.value] : null + if (col.value.startsWith('history_field_')) { + const fieldName = col.value.replace('history_field_', '') + const field = item.auxiliary_data?.find((field: any) => field.name === fieldName) + if (field && !Array.isArray(field.value)) return `${Math.round(field.value * 1000) / 1000} ${field.units}` + } if (value === null) return '--' if (col.value === 'slicer') value += '
' + item.metadata.slicer_version diff --git a/src/components/panels/History/HistoryListRow.vue b/src/components/panels/History/HistoryListRow.vue deleted file mode 100644 index 93214a4c6..000000000 --- a/src/components/panels/History/HistoryListRow.vue +++ /dev/null @@ -1,226 +0,0 @@ - - diff --git a/src/components/panels/History/HistoryListRowCell.vue b/src/components/panels/History/HistoryListRowCell.vue deleted file mode 100644 index c651b08a0..000000000 --- a/src/components/panels/History/HistoryListRowCell.vue +++ /dev/null @@ -1,61 +0,0 @@ - - diff --git a/src/components/panels/HistoryListPanel.vue b/src/components/panels/HistoryListPanel.vue index 99aff2093..10cd90fba 100644 --- a/src/components/panels/HistoryListPanel.vue +++ b/src/components/panels/HistoryListPanel.vue @@ -409,6 +409,16 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { }, ] + this.moonrakerSensors.forEach((sensor) => { + headers.push({ + text: sensor.desc, + value: sensor.name, + align: 'left', + configable: true, + visible: false, + }) + }) + headers.forEach((header) => { if (header.visible && this.hideColums.includes(header.value)) { header.visible = false @@ -420,6 +430,32 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { return headers } + get moonrakerSensors() { + const config = this.$store.state.server.config?.config ?? {} + const sensors = Object.keys(config).filter((key) => key.startsWith('sensor ')) + const historyFields: { desc: string; unit: string; provider: string; name: string; parameter: string }[] = [] + + sensors.forEach((configName) => { + const sensor = config[configName] ?? {} + + Object.keys(sensor) + .filter((key) => key.startsWith('history_field_')) + .forEach((key) => { + const historyField = sensor[key] + + historyFields.push({ + desc: historyField.desc, + unit: historyField.units, + provider: configName, + parameter: historyField.parameter, + name: key, + }) + }) + }) + + return historyFields + } + get tableFields() { return this.filteredHeaders.filter( (col: any) => !['filename', 'status'].includes(col.value) && col.value !== '' @@ -544,6 +580,12 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { row.push('status') this.tableFields.forEach((col) => { + if (col.value.startsWith('history_field_')) { + const sensorName = col.value.replace('history_field_', '') + row.push(sensorName) + return + } + row.push(col.value) }) @@ -612,18 +654,9 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { row.push('job') row.push(job.status) - this.tableFields - .filter((header) => header.value !== 'slicer') - .forEach((col) => { - row.push(this.outputValue(col, job, csvSeperator)) - }) - - if (this.tableFields.find((header) => header.value === 'slicer')?.visible) { - let slicerString = 'slicer' in job.metadata && job.metadata.slicer ? job.metadata.slicer : '--' - if ('slicer_version' in job.metadata && job.metadata.slicer_version) - slicerString += ' ' + job.metadata.slicer_version - row.push(slicerString) - } + this.tableFields.forEach((col) => { + row.push(this.outputValue(col, job, csvSeperator)) + }) content.push(row) }) @@ -634,7 +667,7 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { const csvContent = 'data:text/csv;charset=utf-8,' + content.map((entry) => - entry.map((field) => (field.indexOf(csvSeperator) === -1 ? field : `"${field}"`)).join(csvSeperator) + entry.map((field) => (field?.indexOf(csvSeperator) === -1 ? field : `"${field}"`)).join(csvSeperator) ).join('\n') const link = document.createElement('a') @@ -651,6 +684,36 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { let value = col.value in job ? job[col.value] : null if (value === null) value = col.value in job.metadata ? job.metadata[col.value] : null + if (col.value === 'slicer') { + let slicerString = 'slicer' in job.metadata && job.metadata.slicer ? job.metadata.slicer : '--' + if ('slicer_version' in job.metadata && job.metadata.slicer_version) + slicerString += ' ' + job.metadata.slicer_version + + if (csvSeperator !== null && value.includes(csvSeperator)) return '"' + slicerString + '"' + + return slicerString + } + + if (col.value.startsWith('history_field_')) { + const sensorName = col.value.replace('history_field_', '') + const sensor = job.auxiliary_data?.find((sensor) => sensor.name === sensorName) + + let value = sensor?.value?.toString() + + // return value, when it is not an array + if (sensor && !Array.isArray(sensor.value)) { + value = sensor.value?.toLocaleString(this.browserLocale, { useGrouping: false }) ?? 0 + } + + // return empty string, when value is null + if (!value) return '--' + + // escape fields with the csvSeperator in the content + if (csvSeperator !== null && value?.includes(csvSeperator)) return `"${value}"` + + return value + } + switch (col.outputType) { case 'date': return this.formatDateTime(value * 1000) diff --git a/src/store/server/history/types.ts b/src/store/server/history/types.ts index 4d8cd56fc..4cebfb17a 100644 --- a/src/store/server/history/types.ts +++ b/src/store/server/history/types.ts @@ -50,6 +50,15 @@ export interface ServerHistoryStateJob { status: string start_time: number total_duration: number + auxiliary_data?: ServerHistoryStateJobAuxiliaryData[] +} + +export interface ServerHistoryStateJobAuxiliaryData { + description: string + name: string + provider: string + units: string + value: number | number[] } export interface HistoryListRowJob extends ServerHistoryStateJob { diff --git a/src/store/server/index.ts b/src/store/server/index.ts index 1fde111a3..5e90dc7dc 100644 --- a/src/store/server/index.ts +++ b/src/store/server/index.ts @@ -12,6 +12,7 @@ import { timelapse } from '@/store/server/timelapse' import { jobQueue } from '@/store/server/jobQueue' import { announcements } from '@/store/server/announcements' import { spoolman } from '@/store/server/spoolman' +import { sensor } from '@/store/server/sensor' // create getDefaultState export const getDefaultState = (): ServerState => { @@ -62,5 +63,6 @@ export const server: Module = { jobQueue, announcements, spoolman, + sensor, }, } diff --git a/src/store/server/sensor/actions.ts b/src/store/server/sensor/actions.ts new file mode 100644 index 000000000..28addc301 --- /dev/null +++ b/src/store/server/sensor/actions.ts @@ -0,0 +1,26 @@ +import Vue from 'vue' +import { ActionTree } from 'vuex' +import { ServerSensorState } from '@/store/server/sensor/types' +import { RootState } from '@/store/types' + +export const actions: ActionTree = { + reset({ commit }) { + commit('reset') + }, + + init() { + Vue.$socket.emit('server.sensors.list', {}, { action: 'server/sensor/getSensors' }) + }, + + getSensors({ commit, dispatch }, payload) { + commit('setSensors', payload.sensors) + + dispatch('socket/removeInitModule', 'server/sensor/init', { root: true }) + }, + + updateSensors({ commit }, payload) { + Object.keys(payload).forEach((key) => { + commit('updateSensor', { key, value: payload[key] }) + }) + }, +} diff --git a/src/store/server/sensor/getters.ts b/src/store/server/sensor/getters.ts new file mode 100644 index 000000000..b0b84ee3c --- /dev/null +++ b/src/store/server/sensor/getters.ts @@ -0,0 +1,5 @@ +import { GetterTree } from 'vuex' +import { ServerSensorState } from '@/store/server/sensor/types' + +// eslint-disable-next-line +export const getters: GetterTree = {} diff --git a/src/store/server/sensor/index.ts b/src/store/server/sensor/index.ts new file mode 100644 index 000000000..c60aea2c9 --- /dev/null +++ b/src/store/server/sensor/index.ts @@ -0,0 +1,23 @@ +import { Module } from 'vuex' +import { ServerSensorState } from '@/store/server/sensor/types' +import { actions } from '@/store/server/sensor/actions' +import { mutations } from '@/store/server/sensor/mutations' +import { getters } from '@/store/server/sensor/getters' + +export const getDefaultState = (): ServerSensorState => { + return { + sensors: {}, + } +} + +// initial state +const state = getDefaultState() + +// eslint-disable-next-line +export const sensor: Module = { + namespaced: true, + state, + getters, + actions, + mutations, +} diff --git a/src/store/server/sensor/mutations.ts b/src/store/server/sensor/mutations.ts new file mode 100644 index 000000000..5f4df98f4 --- /dev/null +++ b/src/store/server/sensor/mutations.ts @@ -0,0 +1,18 @@ +import Vue from 'vue' +import { getDefaultState } from './index' +import { MutationTree } from 'vuex' +import { ServerSensorState } from '@/store/server/sensor/types' + +export const mutations: MutationTree = { + reset(state) { + Object.assign(state, getDefaultState()) + }, + + setSensors(state, payload) { + Vue.set(state, 'sensors', payload) + }, + + updateSensor(state, payload) { + Vue.set(state.sensors, payload.key, payload.value) + }, +} diff --git a/src/store/server/sensor/types.ts b/src/store/server/sensor/types.ts new file mode 100644 index 000000000..e745b04d3 --- /dev/null +++ b/src/store/server/sensor/types.ts @@ -0,0 +1,14 @@ +export interface ServerSensorState { + sensors: { + [key: string]: ServerSensorStateSensor + } +} + +export interface ServerSensorStateSensor { + friendly_name: string + id: string + type: string + values: { + [key: string]: number + } +} diff --git a/src/store/socket/actions.ts b/src/store/socket/actions.ts index 50b66b16b..0a207d48b 100644 --- a/src/store/socket/actions.ts +++ b/src/store/socket/actions.ts @@ -135,6 +135,10 @@ export const actions: ActionTree = { dispatch('server/spoolman/getActiveSpoolId', payload.params[0], { root: true }) break + case 'notify_sensor_update': + dispatch('server/sensor/updateSensors', payload.params[0], { root: true }) + break + default: window.console.debug(payload) } diff --git a/src/store/variables.ts b/src/store/variables.ts index f3676d781..4db839504 100644 --- a/src/store/variables.ts +++ b/src/store/variables.ts @@ -35,6 +35,7 @@ export const initableServerComponents = [ 'jobQueue', 'announcements', 'spoolman', + 'sensor', ] /* From 4cf160735bf0feeca8a435708e576d25d88255bd Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 25 May 2024 21:01:22 +0200 Subject: [PATCH 06/45] feat(history): add moonraker sensors to total statistic (#1886) --- src/components/mixins/history.ts | 31 ++++ src/components/panels/HistoryListPanel.vue | 31 +--- .../panels/HistoryStatisticsPanel.vue | 159 +++++++++++++----- src/store/server/history/actions.ts | 5 + src/store/server/history/index.ts | 1 + src/store/server/history/mutations.ts | 4 + src/store/server/history/types.ts | 8 + 7 files changed, 165 insertions(+), 74 deletions(-) create mode 100644 src/components/mixins/history.ts diff --git a/src/components/mixins/history.ts b/src/components/mixins/history.ts new file mode 100644 index 000000000..80b763aac --- /dev/null +++ b/src/components/mixins/history.ts @@ -0,0 +1,31 @@ +import Vue from 'vue' +import Component from 'vue-class-component' + +@Component +export default class HistoryMixin extends Vue { + get moonrakerHistoryFields() { + const config = this.$store.state.server.config?.config ?? {} + const sensors = Object.keys(config).filter((key) => key.startsWith('sensor ')) + const historyFields: { desc: string; unit: string; provider: string; name: string; parameter: string }[] = [] + + sensors.forEach((configName) => { + const sensor = config[configName] ?? {} + + Object.keys(sensor) + .filter((key) => key.startsWith('history_field_')) + .forEach((key) => { + const historyField = sensor[key] + + historyFields.push({ + desc: historyField.desc, + unit: historyField.units, + provider: configName, + parameter: historyField.parameter, + name: key, + }) + }) + }) + + return historyFields + } +} diff --git a/src/components/panels/HistoryListPanel.vue b/src/components/panels/HistoryListPanel.vue index 10cd90fba..85b2b404f 100644 --- a/src/components/panels/HistoryListPanel.vue +++ b/src/components/panels/HistoryListPanel.vue @@ -178,6 +178,7 @@ import HistoryListPanelAddMaintenance from '@/components/dialogs/HistoryListPane import { GuiMaintenanceStateEntry, HistoryListRowMaintenance } from '@/store/gui/maintenance/types' import HistoryListEntryMaintenance from '@/components/panels/History/HistoryListEntryMaintenance.vue' import HistoryListPanelDeleteSelectedDialog from '@/components/dialogs/HistoryListPanelDeleteSelectedDialog.vue' +import HistoryMixin from '@/components/mixins/history' export type HistoryListPanelRow = HistoryListRowJob | HistoryListRowMaintenance @@ -201,7 +202,7 @@ export interface HistoryListPanelCol { Panel, }, }) -export default class HistoryListPanel extends Mixins(BaseMixin) { +export default class HistoryListPanel extends Mixins(BaseMixin, HistoryMixin) { mdiCloseThick = mdiCloseThick mdiCog = mdiCog mdiDatabaseArrowDownOutline = mdiDatabaseArrowDownOutline @@ -409,7 +410,7 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { }, ] - this.moonrakerSensors.forEach((sensor) => { + this.moonrakerHistoryFields.forEach((sensor) => { headers.push({ text: sensor.desc, value: sensor.name, @@ -430,32 +431,6 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { return headers } - get moonrakerSensors() { - const config = this.$store.state.server.config?.config ?? {} - const sensors = Object.keys(config).filter((key) => key.startsWith('sensor ')) - const historyFields: { desc: string; unit: string; provider: string; name: string; parameter: string }[] = [] - - sensors.forEach((configName) => { - const sensor = config[configName] ?? {} - - Object.keys(sensor) - .filter((key) => key.startsWith('history_field_')) - .forEach((key) => { - const historyField = sensor[key] - - historyFields.push({ - desc: historyField.desc, - unit: historyField.units, - provider: configName, - parameter: historyField.parameter, - name: key, - }) - }) - }) - - return historyFields - } - get tableFields() { return this.filteredHeaders.filter( (col: any) => !['filename', 'status'].includes(col.value) && col.value !== '' diff --git a/src/components/panels/HistoryStatisticsPanel.vue b/src/components/panels/HistoryStatisticsPanel.vue index 628bb12c6..a2099cca2 100644 --- a/src/components/panels/HistoryStatisticsPanel.vue +++ b/src/components/panels/HistoryStatisticsPanel.vue @@ -9,50 +9,10 @@ - - + + {{ total.title }} + {{ total.value }} + @@ -104,13 +64,14 @@ import Panel from '@/components/ui/Panel.vue' import HistoryFilamentUsage from '@/components/charts/HistoryFilamentUsage.vue' import HistoryPrinttimeAvg from '@/components/charts/HistoryPrinttimeAvg.vue' import HistoryAllPrintStatusChart from '@/components/charts/HistoryAllPrintStatusChart.vue' -import { ServerHistoryStateJob } from '@/store/server/history/types' +import { ServerHistoryStateJob, ServerHistoryStateJobAuxiliaryTotal } from '@/store/server/history/types' import { mdiChartAreaspline, mdiDatabaseArrowDownOutline } from '@mdi/js' import { formatPrintTime } from '@/plugins/helpers' +import HistoryMixin from '@/components/mixins/history' @Component({ components: { Panel, HistoryFilamentUsage, HistoryPrinttimeAvg, HistoryAllPrintStatusChart }, }) -export default class HistoryStatisticsPanel extends Mixins(BaseMixin) { +export default class HistoryStatisticsPanel extends Mixins(BaseMixin, HistoryMixin) { mdiChartAreaspline = mdiChartAreaspline mdiDatabaseArrowDownOutline = mdiDatabaseArrowDownOutline formatPrintTime = formatPrintTime @@ -215,6 +176,112 @@ export default class HistoryStatisticsPanel extends Mixins(BaseMixin) { return this.$store.state.server.history.all_loaded ?? false } + get selectedTotals() { + const output: { title: string; value: string }[] = [ + { + title: this.$t('History.SelectedPrinttime') as string, + value: this.formatPrintTime(this.selectedPrintTime, false), + }, + { + title: this.$t('History.LongestPrinttime') as string, + value: this.formatPrintTime(this.selectedLongestPrintTime, false), + }, + { + title: this.$t('History.AvgPrinttime') as string, + value: this.formatPrintTime(this.selectedAvgPrintTime, false), + }, + { + title: this.$t('History.SelectedFilamentUsed') as string, + value: this.selectedFilamentUsedFormat, + }, + { + title: this.$t('History.SelectedJobs') as string, + value: this.selectedJobs.length.toString(), + }, + ] + + output.push(...this.auxiliarySelectedTotals) + + return output + } + + get auxiliarySelectedTotals() { + const output: { title: string; value: string }[] = [] + this.moonrakerHistoryFields.forEach((historyField) => { + const value = this.selectedJobs.reduce((acc: number, job: ServerHistoryStateJob) => { + const historyFieldName = historyField.name.replace('history_field_', '') + const auxiliary_data = job.auxiliary_data?.find( + (auxiliary) => auxiliary.provider === historyField.provider && auxiliary.name === historyFieldName + ) + + if (!auxiliary_data || typeof auxiliary_data.value !== 'number') return acc + + return acc + auxiliary_data.value + }, 0) + + output.push({ + title: historyField.desc, + value: `${Math.round(value * 1000) / 1000} ${historyField.unit}`, + }) + }) + + return output + } + + get genericTotals() { + const output: { title: string; value: string }[] = [ + { + title: this.$t('History.TotalPrinttime') as string, + value: this.formatPrintTime(this.totalPrintTime, false), + }, + { + title: this.$t('History.LongestPrinttime') as string, + value: this.formatPrintTime(this.longestPrintTime, false), + }, + { + title: this.$t('History.AvgPrinttime') as string, + value: this.formatPrintTime(this.avgPrintTime, false), + }, + { + title: this.$t('History.TotalFilamentUsed') as string, + value: this.totalFilamentUsedFormat, + }, + { + title: this.$t('History.TotalJobs') as string, + value: this.totalJobsCount.toString(), + }, + ] + + // Add auxiliary totals + output.push(...this.auxiliaryTotals) + + return output + } + + get auxiliaryTotals() { + const auxiliaries = this.$store.state.server.history.auxiliary_totals ?? [] + const output: { title: string; value: string }[] = [] + + auxiliaries.forEach((auxiliary: ServerHistoryStateJobAuxiliaryTotal) => { + const historyFieldName = `history_field_${auxiliary.field}` + const historyField = this.moonrakerHistoryFields.find( + (historyField) => historyField.provider === auxiliary.provider && historyField.name === historyFieldName + ) + const value = Math.round((auxiliary.total ?? 0) * 1000) / 1000 + + output.push({ + title: historyField?.desc ?? auxiliary.field, + value: `${value} ${historyField?.unit}`, + }) + }) + + return output + } + + get totals() { + return this.existsSelectedJobs ? this.selectedTotals : this.genericTotals + } + refreshHistory() { this.$store.dispatch('socket/addLoading', { name: 'historyLoadAll' }) diff --git a/src/store/server/history/actions.ts b/src/store/server/history/actions.ts index 455fc8c4c..3a2eda0cf 100644 --- a/src/store/server/history/actions.ts +++ b/src/store/server/history/actions.ts @@ -19,6 +19,11 @@ export const actions: ActionTree = { getTotals({ commit }, payload) { commit('setTotals', payload.job_totals) + + const auxiliary_totals = payload.auxiliary_totals ?? [] + if (auxiliary_totals.length) { + commit('setAuxiliaryTotals', auxiliary_totals) + } }, async getHistory({ commit, dispatch, state }, payload) { diff --git a/src/store/server/history/index.ts b/src/store/server/history/index.ts index 8e1acead3..c16abbfd5 100644 --- a/src/store/server/history/index.ts +++ b/src/store/server/history/index.ts @@ -15,6 +15,7 @@ export const getDefaultState = (): ServerHistoryState => { longest_job: 0, longest_print: 0, }, + auxiliary_totals: [], all_loaded: false, } } diff --git a/src/store/server/history/mutations.ts b/src/store/server/history/mutations.ts index 9ee7a3129..d4df8fded 100644 --- a/src/store/server/history/mutations.ts +++ b/src/store/server/history/mutations.ts @@ -16,6 +16,10 @@ export const mutations: MutationTree = { Vue.set(state, 'job_totals', payload) }, + setAuxiliaryTotals(state, payload) { + Vue.set(state, 'auxiliary_totals', payload) + }, + setHistoryNotes(state, payload) { const job = state.jobs.find((job) => job.job_id === payload.job_id) if (job) Vue.set(job, 'note', payload.text) diff --git a/src/store/server/history/types.ts b/src/store/server/history/types.ts index 4cebfb17a..d9e69e5fa 100644 --- a/src/store/server/history/types.ts +++ b/src/store/server/history/types.ts @@ -10,6 +10,7 @@ export interface ServerHistoryState { longest_job: number longest_print: number } + auxiliary_totals: ServerHistoryStateJobAuxiliaryTotal[] all_loaded: boolean } @@ -61,6 +62,13 @@ export interface ServerHistoryStateJobAuxiliaryData { value: number | number[] } +export interface ServerHistoryStateJobAuxiliaryTotal { + field: string + maximum: number + provider: string + total: number +} + export interface HistoryListRowJob extends ServerHistoryStateJob { type: 'job' select_id: string From f96e00608058f39eef577bd980d94b88b34a1f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rolf=20Sch=C3=A4uble?= Date: Mon, 3 Jun 2024 20:45:51 +0200 Subject: [PATCH 07/45] fix: consecutive and leading whitespace is not shown in console (#1896) --- src/components/console/ConsoleTableEntry.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/console/ConsoleTableEntry.vue b/src/components/console/ConsoleTableEntry.vue index 9e0cd7897..21c2af4d9 100644 --- a/src/components/console/ConsoleTableEntry.vue +++ b/src/components/console/ConsoleTableEntry.vue @@ -53,6 +53,7 @@ export default class ConsoleTableEntry extends Mixins(BaseMixin) { .consoleTableRow { font-family: 'Roboto Mono', monospace; font-size: 0.95em; + white-space: pre-wrap; &.default { .col { From cec28d06f6ab45a02a85642a7f514382f26cfe5a Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 8 Jun 2024 23:41:18 +0200 Subject: [PATCH 08/45] fix(webcam): fix fps output in light mode (#1901) Signed-off-by: Stefan Dej --- src/components/webcams/streamers/Mjpegstreamer.vue | 4 ++++ src/components/webcams/streamers/MjpegstreamerAdaptive.vue | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/components/webcams/streamers/Mjpegstreamer.vue b/src/components/webcams/streamers/Mjpegstreamer.vue index 89f2a887c..d42ecf78d 100644 --- a/src/components/webcams/streamers/Mjpegstreamer.vue +++ b/src/components/webcams/streamers/Mjpegstreamer.vue @@ -257,4 +257,8 @@ export default class Mjpegstreamer extends Mixins(BaseMixin, WebcamMixin) { padding: 3px 10px; border-top-left-radius: 5px; } + +html.theme--light .webcamFpsOutput { + background: rgba(255, 255, 255, 0.7); +} diff --git a/src/components/webcams/streamers/MjpegstreamerAdaptive.vue b/src/components/webcams/streamers/MjpegstreamerAdaptive.vue index b2dd821b2..39f04cc30 100644 --- a/src/components/webcams/streamers/MjpegstreamerAdaptive.vue +++ b/src/components/webcams/streamers/MjpegstreamerAdaptive.vue @@ -227,4 +227,8 @@ export default class MjpegstreamerAdaptive extends Mixins(BaseMixin, WebcamMixin padding: 3px 10px; border-top-left-radius: 5px; } + +html.theme--light .webcamFpsOutput { + background: rgba(255, 255, 255, 0.7); +} From cf722ea35b1560ed863862139baad28e8a2e64a4 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 8 Jun 2024 23:41:44 +0200 Subject: [PATCH 09/45] fix: fix duration format function (#1894) Signed-off-by: Stefan Dej --- src/components/panels/Status/PrintstatusPrinting.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/panels/Status/PrintstatusPrinting.vue b/src/components/panels/Status/PrintstatusPrinting.vue index 2f19780ca..400b96d42 100644 --- a/src/components/panels/Status/PrintstatusPrinting.vue +++ b/src/components/panels/Status/PrintstatusPrinting.vue @@ -239,13 +239,13 @@ export default class StatusPanelPrintstatusPrinting extends Mixins(BaseMixin) { } formatDuration(seconds: number) { - let prefix = seconds < 0 ? '-' : '' + const prefix = seconds < 0 ? '-' : '' let absSeconds = Math.abs(seconds) - let h = Math.floor(absSeconds / 3600) + const h = Math.floor(absSeconds / 3600) absSeconds %= 3600 - let m = ('0' + Math.floor(absSeconds / 60)).slice(-2) - let s = ('0' + (absSeconds % 60).toFixed(0)).slice(-2) + const m = ('0' + Math.floor(absSeconds / 60)).slice(-2) + const s = ('0' + Math.floor(absSeconds % 60)).slice(-2) return prefix + h + ':' + m + ':' + s } From 6b625acf1a4ee43fbffd7885e3fc0637bf9f60d4 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 8 Jun 2024 23:42:15 +0200 Subject: [PATCH 10/45] refactor(macros): refactor gcode_macros getter (#1889) * refactor(macros): refactor gcode_macros getter Signed-off-by: Stefan Dej * fix: fix macros without a description Signed-off-by: Stefan Dej * fix: fix help text for gcode macros Signed-off-by: Stefan Dej * refactor: remove magic numbers Signed-off-by: Stefan Dej --------- Signed-off-by: Stefan Dej --- src/store/printer/getters.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/store/printer/getters.ts b/src/store/printer/getters.ts index 225b06bee..ba2f3d6d9 100644 --- a/src/store/printer/getters.ts +++ b/src/store/printer/getters.ts @@ -141,24 +141,31 @@ export const getters: GetterTree = { getMacros: (state) => { const array: PrinterStateMacro[] = [] - const config = state.configfile?.config ?? {} const settings = state.configfile?.settings ?? null + const printerGcodes = state.gcode?.commands ?? {} - Object.keys(config) - .filter((prop) => prop.toLowerCase().startsWith('gcode_macro')) + const prefix = 'gcode_macro ' + const prefixLength = prefix.length + + Object.keys(state) + .filter((prop) => prop.toLowerCase().startsWith(prefix)) .forEach((prop) => { - const name = prop.replace('gcode_macro ', '') + const name = prop.slice(prefixLength) + const printerGcode = printerGcodes[name.toUpperCase()] ?? {} + + // remove macros with a '_' as first char if (name.startsWith('_')) return + // remove macros with rename_existing in the config const propLower = prop.toLowerCase() - const propSettings = settings[propLower] + const propSettings = settings[propLower] ?? {} if ('rename_existing' in propSettings) return const variables = state[prop] ?? {} array.push({ name, - description: settings[propLower].description ?? null, + description: printerGcode?.help ?? null, prop: propSettings, params: getMacroParams(propSettings), variables, From 113d0790a5034c91c4c42560e6ed5707a7b93c3f Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sun, 9 Jun 2024 19:51:28 +0200 Subject: [PATCH 11/45] feat: add support for base url (#1873) --- src/main.ts | 5 ++++- src/plugins/router.ts | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main.ts b/src/main.ts index 269588c33..13127500a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -66,8 +66,11 @@ Vue.use(VueResize) const initLoad = async () => { try { + // get base url. by default, it is '/' + const base = import.meta.env.BASE_URL ?? '/' + //load config.json - const res = await fetch('/config.json') + const res = await fetch(`${base}config.json`) const file = (await res.json()) as Record window.console.debug('Loaded config.json') diff --git a/src/plugins/router.ts b/src/plugins/router.ts index f211d1918..2d31ce432 100644 --- a/src/plugins/router.ts +++ b/src/plugins/router.ts @@ -3,6 +3,10 @@ import Vue from 'vue' import routes from '@/routes' Vue.use(VueRouter) -const router = new VueRouter({ mode: 'history', routes }) +const router = new VueRouter({ + base: import.meta.env.BASE_URL, + mode: 'history', + routes, +}) export default router From 421d2b137d819847b8e42ac216f4d50ff4e1028c Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sun, 9 Jun 2024 20:10:33 +0200 Subject: [PATCH 12/45] feat(dashboard): add support for moonraker sensor (#1888) Co-authored-by: Patrick Stainbrook --- .../panels/Miscellaneous/MoonrakerSensor.vue | 48 ++++++++++ .../Miscellaneous/MoonrakerSensorValue.vue | 92 +++++++++++++++++++ src/components/panels/MiscellaneousPanel.vue | 21 +++-- src/store/server/sensor/getters.ts | 6 +- src/store/server/sensor/mutations.ts | 4 +- 5 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 src/components/panels/Miscellaneous/MoonrakerSensor.vue create mode 100644 src/components/panels/Miscellaneous/MoonrakerSensorValue.vue diff --git a/src/components/panels/Miscellaneous/MoonrakerSensor.vue b/src/components/panels/Miscellaneous/MoonrakerSensor.vue new file mode 100644 index 000000000..e0b919518 --- /dev/null +++ b/src/components/panels/Miscellaneous/MoonrakerSensor.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/src/components/panels/Miscellaneous/MoonrakerSensorValue.vue b/src/components/panels/Miscellaneous/MoonrakerSensorValue.vue new file mode 100644 index 000000000..1ac9f31c2 --- /dev/null +++ b/src/components/panels/Miscellaneous/MoonrakerSensorValue.vue @@ -0,0 +1,92 @@ + + + diff --git a/src/components/panels/MiscellaneousPanel.vue b/src/components/panels/MiscellaneousPanel.vue index 9a671d075..976e4cf88 100644 --- a/src/components/panels/MiscellaneousPanel.vue +++ b/src/components/panels/MiscellaneousPanel.vue @@ -8,7 +8,7 @@ :collapsible="true" card-class="miscellaneous-panel">
- + + :multi="parseInt(object.scale)" />
- +
- + + :filament_detected="sensor.filament_detected" /> +
+
+ +
@@ -49,10 +53,11 @@ import BaseMixin from '@/components/mixins/base' import MiscellaneousSlider from '@/components/inputs/MiscellaneousSlider.vue' import MiscellaneousLight from '@/components/inputs/MiscellaneousLight.vue' import FilamentSensor from '@/components/inputs/FilamentSensor.vue' +import MoonrakerSensor from '@/components/panels/Miscellaneous/MoonrakerSensor.vue' import Panel from '@/components/ui/Panel.vue' import { mdiDipSwitch } from '@mdi/js' @Component({ - components: { Panel, FilamentSensor, MiscellaneousSlider, MiscellaneousLight }, + components: { Panel, FilamentSensor, MiscellaneousSlider, MiscellaneousLight, MoonrakerSensor }, }) export default class MiscellaneousPanel extends Mixins(BaseMixin) { mdiDipSwitch = mdiDipSwitch @@ -69,6 +74,10 @@ export default class MiscellaneousPanel extends Mixins(BaseMixin) { return this.$store.getters['printer/getLights'] ?? [] } + get moonrakerSensors() { + return this.$store.getters['server/sensor/getSensors'] ?? [] + } + get showMiscellaneousPanel() { return ( this.klipperReadyForGui && (this.miscellaneous.length || this.filamentSensors.length || this.lights.length) diff --git a/src/store/server/sensor/getters.ts b/src/store/server/sensor/getters.ts index b0b84ee3c..10028f160 100644 --- a/src/store/server/sensor/getters.ts +++ b/src/store/server/sensor/getters.ts @@ -2,4 +2,8 @@ import { GetterTree } from 'vuex' import { ServerSensorState } from '@/store/server/sensor/types' // eslint-disable-next-line -export const getters: GetterTree = {} +export const getters: GetterTree = { + getSensors: (state) => { + return Object.keys(state.sensors) + }, +} diff --git a/src/store/server/sensor/mutations.ts b/src/store/server/sensor/mutations.ts index 5f4df98f4..2e9a7b841 100644 --- a/src/store/server/sensor/mutations.ts +++ b/src/store/server/sensor/mutations.ts @@ -13,6 +13,8 @@ export const mutations: MutationTree = { }, updateSensor(state, payload) { - Vue.set(state.sensors, payload.key, payload.value) + if (!(payload.key in state.sensors)) return + + Vue.set(state.sensors[payload.key], 'values', payload.value) }, } From b75f842c39bba945ccdd3c1d0b8035fb48e6a77f Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Fri, 14 Jun 2024 22:35:26 +0200 Subject: [PATCH 13/45] fix: update moonraker log path in TheConnectingDialog.vue (#1909) Signed-off-by: Stefan Dej --- src/components/TheConnectingDialog.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/TheConnectingDialog.vue b/src/components/TheConnectingDialog.vue index 29cad445b..d32bdd7c9 100644 --- a/src/components/TheConnectingDialog.vue +++ b/src/components/TheConnectingDialog.vue @@ -18,8 +18,7 @@

{{ $t('ConnectionDialog.CheckMoonrakerLog') }}

    -
  • ~/klipper_logs/moonraker.log
  • -
  • /tmp/moonraker.log
  • +
  • ~/printer_data/logs/moonraker.log
From f6fba3f21b9e6f24f80cdaaf00e03be0aaaae1c7 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Fri, 14 Jun 2024 23:22:07 +0200 Subject: [PATCH 14/45] feat(systemLoads): add function to output app name in system loads panel (#1906) Signed-off-by: Stefan Dej --- src/store/printer/actions.ts | 1 + src/store/server/getters.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/store/printer/actions.ts b/src/store/printer/actions.ts index a2af4be52..646953616 100644 --- a/src/store/printer/actions.ts +++ b/src/store/printer/actions.ts @@ -37,6 +37,7 @@ export const actions: ActionTree = { ) commit('setData', { + app_name: payload.app ?? null, hostname: payload.hostname, software_version: payload.software_version, cpu_info: payload.cpu_info, diff --git a/src/store/server/getters.ts b/src/store/server/getters.ts index d10bd4000..8dd86f71b 100644 --- a/src/store/server/getters.ts +++ b/src/store/server/getters.ts @@ -77,6 +77,10 @@ export const getters: GetterTree = { version = rootState.printer?.software_version.split('-').slice(0, 4).join('-') } + if (rootState.printer?.app_name) { + version = rootState.printer?.app_name + ' ' + version + } + let pythonVersion: null | string = null if (state.system_info?.python?.version_string) { const firstSpace = state.system_info?.python?.version_string.indexOf(' ') From 39e10579b1f542386aeb3b500c96ace8c803a3d7 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Fri, 14 Jun 2024 23:22:34 +0200 Subject: [PATCH 15/45] fix(systemLoads): fix temp output when no temp sensor was found in klipper (#1907) Signed-off-by: Stefan Dej --- src/components/panels/Machine/SystemPanelHost.vue | 4 +++- src/store/server/getters.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/panels/Machine/SystemPanelHost.vue b/src/components/panels/Machine/SystemPanelHost.vue index affe497bd..fe9a75d54 100644 --- a/src/components/panels/Machine/SystemPanelHost.vue +++ b/src/components/panels/Machine/SystemPanelHost.vue @@ -39,7 +39,7 @@ {{ $t('Machine.SystemPanel.Values.Load', { load: hostStats.load }) }}, - {{ $t('Machine.SystemPanel.Values.Memory', { memory: hostStats.memoryFormat }) }}, + {{ $t('Machine.SystemPanel.Values.Memory', { memory: hostStats.memoryFormat }) }}