Skip to content

Commit b56ca8d

Browse files
feat: improve notification UX and PM2 error handling
1 parent 08e3837 commit b56ca8d

File tree

4 files changed

+63
-22
lines changed

4 files changed

+63
-22
lines changed

node_helper.js

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,37 +1022,53 @@ module.exports = NodeHelper.create({
10221022
},
10231023

10241024
controlPm2 (res, query) {
1025-
try { require("pm2"); } catch (err) {
1026-
this.sendResponse(res, err, {reason: "PM2 not installed or unlinked"});
1025+
const actionName = query.action.toLowerCase();
1026+
1027+
// Check if PM2 is available
1028+
try {
1029+
require.resolve("pm2");
1030+
} catch {
1031+
// PM2 not installed
1032+
const message = `MagicMirror² is not running under PM2. Please ${actionName} manually.`;
1033+
Log.log(`[MMM-Remote-Control] ${message}`);
1034+
this.sendResponse(res, undefined, {action: actionName, info: message, status: "info"});
10271035
return;
10281036
}
1037+
10291038
const pm2 = require("pm2");
10301039
const processName = query.processName || this.thisConfig.pm2ProcessName || "mm";
10311040

10321041
pm2.connect((err) => {
10331042
if (err) {
1034-
this.sendResponse(res, err);
1043+
pm2.disconnect();
1044+
const message = `MagicMirror² is not running under PM2. Please ${actionName} manually.`;
1045+
Log.log(`[MMM-Remote-Control] ${message}`);
1046+
this.sendResponse(res, undefined, {action: actionName, info: message, status: "info"});
10351047
return;
10361048
}
10371049

1038-
const actionName = query.action.toLowerCase();
1039-
Log.log(`[MMM-Remote-Control] PM2 process: ${actionName} ${processName}`);
1050+
// Check if process is running in PM2
1051+
pm2.list((err, list) => {
1052+
if (err || !list.find((proc) => proc.name === processName && proc.pm2_env.status === "online")) {
1053+
pm2.disconnect();
1054+
const message = `MagicMirror² is not running under PM2. Please ${actionName} manually.`;
1055+
Log.log(`[MMM-Remote-Control] ${message}`);
1056+
this.sendResponse(res, undefined, {action: actionName, info: message, status: "info"});
1057+
return;
1058+
}
10401059

1041-
switch (actionName) {
1042-
case "restart":
1043-
pm2.restart(processName, (err) => {
1044-
this.sendResponse(res, undefined, {action: actionName, processName});
1045-
if (err) { this.sendResponse(res, err); }
1046-
});
1047-
break;
1048-
case "stop":
1049-
pm2.stop(processName, (err) => {
1060+
// Process is running in PM2, perform action
1061+
pm2[actionName](processName, (err) => {
1062+
pm2.disconnect();
1063+
if (err) {
1064+
Log.error(`[MMM-Remote-Control] PM2 ${actionName} error:`, err);
1065+
this.sendResponse(res, err);
1066+
} else {
1067+
Log.log(`[MMM-Remote-Control] PM2 ${actionName}: ${processName}`);
10501068
this.sendResponse(res, undefined, {action: actionName, processName});
1051-
pm2.disconnect();
1052-
if (err) { this.sendResponse(res, err); }
1053-
});
1054-
break;
1055-
}
1069+
}
1070+
});
1071+
});
10561072
});
10571073
},
10581074

remote.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ const Remote = {
2929
addModule: "",
3030
changedModules: [],
3131
deletedModules: [],
32-
autoHideTimer: undefined,
33-
autoHideDelay: 1000, // ms
32+
autoHideTimer: undefined, // Internal: Reference to the active auto-hide timeout (do not modify manually)
33+
autoHideDelay: 2000, // ms - Time after which success messages are auto hidden
34+
autoHideDelayError: 30 * 1000, // ms - Time for error messages (0 = no auto-hide, must be clicked away)
35+
autoHideDelayInfo: 30 * 1000, // ms - Time for info messages like PM2 restart/stop
3436

3537
/*
3638
* socket()
@@ -408,7 +410,28 @@ const Remote = {
408410
if (status === "error") {
409411
symbol = "fa-exclamation-circle";
410412
text = this.translate("ERROR");
411-
onClick = false;
413+
onClick = function () {
414+
self.setStatus("none");
415+
};
416+
// Only auto-hide errors if autoHideDelayError > 0, otherwise user must click to dismiss
417+
if (this.autoHideDelayError > 0) {
418+
this.autoHideTimer = setTimeout(() => {
419+
self.setStatus("none");
420+
}, this.autoHideDelayError);
421+
}
422+
}
423+
if (status === "info") {
424+
symbol = "fa-info-circle";
425+
text = this.translate("INFO");
426+
onClick = function () {
427+
self.setStatus("none");
428+
};
429+
// Info messages (like PM2 restart/stop) should be displayed longer
430+
if (this.autoHideDelayInfo > 0) {
431+
this.autoHideTimer = setTimeout(() => {
432+
self.setStatus("none");
433+
}, this.autoHideDelayInfo);
434+
}
412435
}
413436
if (status === "success") {
414437
symbol = "fa-check-circle";

translations/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060

6161
"DONE": "Fertig.",
6262
"ERROR": "Fehler!",
63+
"INFO": "Info",
6364
"LOADING": "Lade …",
6465

6566
"LOCKSTRING_WARNING": "Dieses Modul wurde durch LIST_OF_MODULES versteckt, es kann nicht angezeigt werden.",

translations/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060

6161
"DONE": "Done.",
6262
"ERROR": "Error!",
63+
"INFO": "Info",
6364
"LOADING": "Loading …",
6465

6566
"LOCKSTRING_WARNING": "This module was hidden by LIST_OF_MODULES, it can not be shown.",

0 commit comments

Comments
 (0)