Skip to content

Commit 93b4b81

Browse files
feat: add zoom, background color and font color controls
Extends the existing CSS settings section in the edit menu with: - Zoom slider (30–170% in UI, 30–200% via API, default 100%, center = 100%) - Background color picker with reset button - Font color picker with reset button All three settings are persisted to settings.json and restored on startup. REST API endpoints added: GET /api/zoom/:setting GET /api/backgroundcolor/:color (rrggbb hex or 'reset') GET /api/fontcolor/:color (rrggbb hex or 'reset') Translations added for all 14 supported languages. Swagger docs and README updated. Closes #238
1 parent bcf80f0 commit 93b4b81

29 files changed

Lines changed: 572 additions & 39 deletions

API/api.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,47 @@ const getActions = (handler) => {
3737
return [...availableActions];
3838
};
3939

40+
/**
41+
* Registers color-related API routes (zoom, background color, font color).
42+
* Extracted to keep createApiRoutes within line limits.
43+
* @param {object} router - Express router
44+
* @param {object} ctx - API context (for executeQuery)
45+
*/
46+
function registerColorRoutes (router, ctx) {
47+
router.route(["/zoom/:setting"]).
48+
get((request, res) => {
49+
// Only allow numeric settings, otherwise return 400
50+
if (!(/^\d+$/).test(request.params.setting)) {
51+
return res.status(400).json({success: false, message: "Invalid zoom setting, must be a number (30-200)"});
52+
}
53+
ctx.executeQuery({action: "ZOOM", value: request.params.setting}, res);
54+
});
55+
56+
router.route(["/backgroundcolor/:color"]).
57+
get((request, res) => {
58+
const {color} = request.params;
59+
if (color === "reset") {
60+
return ctx.executeQuery({action: "BACKGROUND_COLOR", value: ""}, res);
61+
}
62+
if (!(/^[\da-f]{6}$/i).test(color)) {
63+
return res.status(400).json({success: false, message: "Invalid color, use rrggbb hex format or 'reset'"});
64+
}
65+
ctx.executeQuery({action: "BACKGROUND_COLOR", value: `#${color}`}, res);
66+
});
67+
68+
router.route(["/fontcolor/:color"]).
69+
get((request, res) => {
70+
const {color} = request.params;
71+
if (color === "reset") {
72+
return ctx.executeQuery({action: "FONT_COLOR", value: ""}, res);
73+
}
74+
if (!(/^[\da-f]{6}$/i).test(color)) {
75+
return res.status(400).json({success: false, message: "Invalid color, use rrggbb hex format or 'reset'"});
76+
}
77+
ctx.executeQuery({action: "FONT_COLOR", value: `#${color}`}, res);
78+
});
79+
}
80+
4081
module.exports = {
4182

4283
/*
@@ -336,6 +377,8 @@ module.exports = {
336377
this.executeQuery({action: "BRIGHTNESS", value: request.params.setting}, res);
337378
});
338379

380+
registerColorRoutes(this.expressRouter, this);
381+
339382
this.expressRouter.route("/timers").get((request, res) => { this.sendResponse(res, undefined, this.delayedQueryTimers); });
340383

341384
this.expressApp.use("/api", this.expressRouter);

MMM-Remote-Control.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ Module.register("MMM-Remote-Control", {
3131

3232
this.brightness = 100;
3333
this.temp = 327;
34+
this.zoom = 100;
35+
this.backgroundColor = "";
36+
this.fontColor = "";
3437

3538
this.qrCodeDataUrl = null;
3639
},
@@ -118,6 +121,18 @@ Module.register("MMM-Remote-Control", {
118121
this.setTemp(Number.parseInt(payload));
119122
break;
120123

124+
case "ZOOM":
125+
this.setZoom(Number.parseInt(payload));
126+
break;
127+
128+
case "BACKGROUND_COLOR":
129+
this.setBackgroundColor(payload);
130+
break;
131+
132+
case "FONT_COLOR":
133+
this.setFontColor(payload);
134+
break;
135+
121136
case "REFRESH":
122137
document.location.reload();
123138
break;
@@ -177,6 +192,36 @@ Module.register("MMM-Remote-Control", {
177192
this.temp = temperature;
178193
},
179194

195+
setZoom (zoom) {
196+
zoom = Math.max(30, Math.min(200, Number.isNaN(zoom) ? 100 : zoom));
197+
document.body.style.zoom = `${zoom}%`;
198+
this.zoom = zoom;
199+
},
200+
201+
setBackgroundColor (color) {
202+
let styleEl = document.getElementById("remote-control-background-color-override");
203+
if (!styleEl) {
204+
styleEl = document.createElement("style");
205+
styleEl.id = "remote-control-background-color-override";
206+
document.head.append(styleEl);
207+
}
208+
styleEl.textContent = color
209+
? `html, body { background-color: ${color} !important; }`
210+
: "";
211+
this.backgroundColor = color;
212+
},
213+
214+
setFontColor (color) {
215+
let styleEl = document.getElementById("remote-control-font-color-override");
216+
if (!styleEl) {
217+
styleEl = document.createElement("style");
218+
styleEl.id = "remote-control-font-color-override";
219+
document.head.append(styleEl);
220+
}
221+
styleEl.textContent = color ? `body * { color: ${color} !important; }` : "";
222+
this.fontColor = color;
223+
},
224+
180225
/**
181226
* Handle DEFAULT_SETTINGS notification - manage module visibility
182227
* @param {object} payload - Settings data
@@ -215,6 +260,9 @@ Module.register("MMM-Remote-Control", {
215260

216261
this.setBrightness(payload.brightness);
217262
this.setTemp(payload.temp);
263+
this.setZoom(payload.zoom ?? 100);
264+
this.setBackgroundColor(payload.backgroundColor ?? "");
265+
this.setFontColor(payload.fontColor ?? "");
218266
},
219267

220268
/**
@@ -323,6 +371,9 @@ Module.register("MMM-Remote-Control", {
323371
moduleData: currentModuleData,
324372
brightness: this.brightness,
325373
temp: this.temp,
374+
zoom: this.zoom,
375+
backgroundColor: this.backgroundColor,
376+
fontColor: this.fontColor,
326377
settingsVersion: this.settingsVersion,
327378
remoteConfig: this.config
328379
};

README.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,17 @@ See the [Examples Guide](docs/guide/examples.md) for more integration examples w
177177
178178
#### MagicMirror² Control
179179
180-
| Action | Description |
181-
| :--------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
182-
| RESTART | Restart MagicMirror² (Electron relaunch or clean process exit for process managers like systemd/PM2/Docker) |
183-
| STOP | Stop MagicMirror² without restarting (exits with error code, process managers won't auto-restart) |
184-
| REFRESH | Refresh mirror page |
185-
| UPDATE | Update MagicMirror² and any of it's modules |
186-
| SAVE | Save the current configuration (show and hide status of modules, and brightness), will be applied after the mirror starts |
187-
| BRIGHTNESS | Change mirror brightness, with the new value specified by `value`. `100` equals the default (full brightness), possible range is between `0` (black) and `100`. |
180+
| Action | Description |
181+
| :--------------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
182+
| RESTART | Restart MagicMirror² (Electron relaunch or clean process exit for process managers like systemd/PM2/Docker) |
183+
| STOP | Stop MagicMirror² without restarting (exits with error code, process managers won't auto-restart) |
184+
| REFRESH | Refresh mirror page |
185+
| UPDATE | Update MagicMirror² and any of it's modules |
186+
| SAVE | Save the current configuration (show and hide status of modules, brightness, zoom, and colors), will be applied after the mirror starts |
187+
| BRIGHTNESS | Change mirror brightness, with the new value specified by `value`. `100` equals the default (full brightness), possible range is between `0` (black) and `100`. |
188+
| ZOOM | Change the page zoom level, with the new value specified by `value`. `100` equals the default (100%), possible range is between `30` and `200`. |
189+
| BACKGROUND_COLOR | Set the background color of the mirror, with the color specified by `value` as a CSS color string (e.g. `#ff0000`). Send an empty string to reset to the default. |
190+
| FONT_COLOR | Set the font color of all elements, with the color specified by `value` as a CSS color string (e.g. `#ffffff`). Send an empty string to reset to the default. |
188191
189192
#### MagicMirror² Electron Browser Window Control
190193
@@ -236,6 +239,9 @@ The response will be in the JSON format, here is an example:
236239
}
237240
],
238241
"brightness": 40,
242+
"zoom": 100,
243+
"backgroundColor": "",
244+
"fontColor": "",
239245
"settingsVersion": 1
240246
}
241247
```

cspell.config.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"articlescrollup",
1212
"articletogglefull",
1313
"Ausschalten",
14+
"backgroundcolor",
1415
"Bethge",
1516
"bokmål",
1617
"busctl",
@@ -25,6 +26,7 @@
2526
"ezeholz",
2627
"Ezequiel",
2728
"Flickr",
29+
"fontcolor",
2830
"FULLSCREEN",
2931
"Hafas",
3032
"HIDEALERT",
@@ -75,6 +77,7 @@
7577
"remotecontrol",
7678
"RESTARTMM",
7779
"revparse",
80+
"rrggbb",
7881
"scrot",
7982
"SENDALERT",
8083
"shbatm",

docs/swagger.json

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@
628628
"REPOSITORY": "Repository",
629629
"BACK": "Back",
630630
"BRIGHTNESS": "Brightness",
631+
"ZOOM": "Zoom",
632+
"BACKGROUND_COLOR": "Background color",
633+
"FONT_COLOR": "Font color",
631634
"REFRESHMM": "Refresh Browser",
632635
"MONITOROFF": "Turn monitor OFF",
633636
"MONITORON": "Turn monitor ON",
@@ -799,6 +802,120 @@
799802
}
800803
}
801804
},
805+
"/api/zoom/{:setting}": {
806+
"get": {
807+
"tags": ["Mirror Control", "API"],
808+
"summary": "Get or set the zoom level of the Mirror by passing a numerical value (30~200)",
809+
"parameters": [
810+
{
811+
"name": ":setting",
812+
"in": "path",
813+
"required": true,
814+
"description": "The zoom level in percent (30-200) or empty to get current zoom",
815+
"schema": {
816+
"type": "integer"
817+
}
818+
}
819+
],
820+
"security": [
821+
{
822+
"apikey": []
823+
}
824+
],
825+
"responses": {
826+
"200": {
827+
"description": "Successfully retrieved or set zoom level",
828+
"content": {
829+
"application/json": {
830+
"example": {
831+
"success": true,
832+
"query": {
833+
"data": "zoom"
834+
},
835+
"result": 100
836+
}
837+
}
838+
}
839+
}
840+
}
841+
}
842+
},
843+
"/api/backgroundcolor/{:color}": {
844+
"get": {
845+
"tags": ["Mirror Control", "API"],
846+
"summary": "Set the background color of the Mirror by passing a hex color value (rrggbb), or 'reset' to clear",
847+
"parameters": [
848+
{
849+
"name": ":color",
850+
"in": "path",
851+
"required": true,
852+
"description": "The background color in rrggbb hex format (e.g. ff0000) or 'reset' to clear",
853+
"schema": {
854+
"type": "string"
855+
}
856+
}
857+
],
858+
"security": [
859+
{
860+
"apikey": []
861+
}
862+
],
863+
"responses": {
864+
"200": {
865+
"description": "Successfully set background color",
866+
"content": {
867+
"application/json": {
868+
"example": {
869+
"success": true,
870+
"query": {
871+
"data": "backgroundColor"
872+
},
873+
"result": "#ff0000"
874+
}
875+
}
876+
}
877+
}
878+
}
879+
}
880+
},
881+
"/api/fontcolor/{:color}": {
882+
"get": {
883+
"tags": ["Mirror Control", "API"],
884+
"summary": "Set the font color of the Mirror by passing a hex color value (rrggbb), or 'reset' to clear",
885+
"parameters": [
886+
{
887+
"name": ":color",
888+
"in": "path",
889+
"required": true,
890+
"description": "The font color in rrggbb hex format (e.g. ffffff) or 'reset' to clear",
891+
"schema": {
892+
"type": "string"
893+
}
894+
}
895+
],
896+
"security": [
897+
{
898+
"apikey": []
899+
}
900+
],
901+
"responses": {
902+
"200": {
903+
"description": "Successfully set font color",
904+
"content": {
905+
"application/json": {
906+
"example": {
907+
"success": true,
908+
"query": {
909+
"data": "fontColor"
910+
},
911+
"result": "#ffffff"
912+
}
913+
}
914+
}
915+
}
916+
}
917+
}
918+
},
802919
"/api/userpresence/{:value}": {
803920
"get": {
804921
"tags": ["Module Control", "API"],

lib/configManager.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ async function loadDefaultSettings (moduleDir) {
192192
/**
193193
* Save default settings to settings.json
194194
* @param {string} moduleDir - Module directory path
195-
* @param {object} configData - Config data containing moduleData, brightness, temp, settingsVersion
195+
* @param {object} configData - Config data containing moduleData, brightness, temp, zoom, backgroundColor, fontColor, settingsVersion
196196
* @returns {Promise<void>}
197197
*/
198198
async function saveDefaultSettings (moduleDir, configData) {
@@ -208,6 +208,9 @@ async function saveDefaultSettings (moduleDir, configData) {
208208
moduleData: simpleModuleData,
209209
brightness: configData.brightness,
210210
temp: configData.temp,
211+
zoom: configData.zoom ?? 100,
212+
backgroundColor: configData.backgroundColor ?? "",
213+
fontColor: configData.fontColor ?? "",
211214
settingsVersion: configData.settingsVersion
212215
});
213216

0 commit comments

Comments
 (0)