Maintainer: Tony Di Bona | Updated: May 2, 2026
Using Raspberry Pi Imager, select Raspberry Pi OS 64-bit (2026-04-21). Click the settings gear and configure:
sudo apt update && sudo apt upgrade -y
sudo visudo
Find this line: %sudo ALL=(ALL:ALL) ALL
Change it to:
%sudo ALL=(ALL:ALL) NOPASSWD: ALL
curl -sL https://deb.nodesource.com/setup_24.x | sudo bash -
sudo apt install -y nodejs
cd ~
git clone https://github.com/MagicMirrorOrg/MagicMirror
cd MagicMirror
npm run install-mm
cp ~/MagicMirror/config/config.js.sample ~/MagicMirror/config/config.js
mkdir -p ~/.config/autostart
vi ~/.config/autostart/magicmirror.desktop
Add this content:
[Desktop Entry]
Type=Application
Name=MagicMirror
Exec=bash -c 'cd /home/pindari/MagicMirror && WAYLAND_DISPLAY=wayland-0 npm start'
X-GNOME-Autostart-enabled=true
vi ~/.config/autostart/randr.desktop
Add this content:
[Desktop Entry]
Type=Application
Name=Display Rotation
Exec=bash -c 'WAYLAND_DISPLAY=wayland-0 wlr-randr --output HDMI-A-1 --transform 90 >> /tmp/randr.log 2>&1'
X-GNOME-Autostart-enabled=true
The Raspberry Pi 4 may not correctly read the monitor's EDID data and fall back to a lower resolution. To force 1920x1080:
sudo vi /boot/firmware/cmdline.txt
Add video=HDMI-A-1:1920x1080@60 to the end of the existing single line, separated by a space. The file must remain a single line.
sudo reboot
Verify the display resolution and rotation:
WAYLAND_DISPLAY=wayland-0 wlr-randr
Confirm that the output shows 1920x1080 px as the current mode and Transform: 90.
Verify Node.js and MagicMirror status:
node -v && npm -v
ps aux | grep electron | grep -v grep
cd ~/MagicMirror/modules
git clone https://github.com/AdamMoses-GitHub/MMM-ImageSlideshow.git
The photos folder already exists as exampleImages inside the module. Upload photos to:
~/MagicMirror/modules/MMM-ImageSlideshow/exampleImages/
Photos should be 1040 x 614 pixels and in 16:9 aspect ratio for best results.
cd ~/MagicMirror/modules
git clone https://github.com/ZachR19/MMM-Todoist2.git
cd MMM-Todoist2
npm install
vi ~/MagicMirror/config/config.js
Replace the entire file with:
let config = {
address: "localhost",
port: 8080,
basePath: "/",
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
useHttps: false,
httpsPrivateKey: "",
httpsCertificate: "",
language: "en",
locale: "en-US",
logLevel: ["INFO", "LOG", "WARN", "ERROR"],
timeFormat: 24,
units: "metric",
modules: [
{
module: "clock",
position: "top_left",
header: "Local Time",
config: {
timezone: "Pacific/Auckland",
timeFormat: 12,
showPeriod: true,
showDate: true,
dateFormat: "ddd, MMM D, YYYY",
displaySeconds: false,
}
},
{
module: "clock",
position: "top_right",
header: "California Time",
config: {
timezone: "America/Los_Angeles",
timeFormat: 12,
showPeriod: true,
showDate: true,
dateFormat: "ddd, MMM D, YYYY",
displaySeconds: false,
}
},
{
module: "MMM-ImageSlideshow",
position: "upper_third",
config: {
imagePaths: ["modules/MMM-ImageSlideshow/exampleImages/"],
slideshowSpeed: 5000,
randomizeImageOrder: true,
fixedImageWidth: 1040,
fixedImageHeight: 614,
}
},
{
module: "weather",
position: "middle_center",
config: {
weatherProvider: "openmeteo",
type: "forecast",
lat: -41.2190,
lon: 175.4601,
maxNumberOfDays: 7,
units: "metric",
showPrecipitationAmount: true,
showPrecipitationProbability: true,
showWindDirection: true,
roundTemp: true,
colored: true,
appendLocationNameToHeader: false,
}
},
{
module: "calendar",
position: "bottom_left",
header: "Upcoming Events",
config: {
maximumEntries: 10,
maximumNumberOfDays: 30,
showLocation: false,
fade: false,
timeFormat: "dateheaders",
appendLocationNameToHeader: false,
calendars: [
{
url: "YOUR_GOOGLE_ICAL_URL",
symbol: "calendar-days",
}
],
}
},
{
module: "MMM-Todoist2",
position: "bottom_right",
header: "Tasks",
config: {
accessToken: "YOUR_TODOIST_API_TOKEN",
maximumEntries: 60,
updateInterval: 10*60*1000,
fade: false,
showProject: false,
sortOrder: ["priority"],
projects: ["YOUR_PROJECT_ID"],
}
},
],
};
if (typeof module !== "undefined") { module.exports = config; }
vi ~/MagicMirror/config/custom.css
Add this content:
:root {
--gap-body-left: 20px;
--gap-body-right: 20px;
}
.clock .module-header {
font-size: 1.65em;
}
.clock .date {
font-size: 2.55em;
}
.clock .time {
font-size: 4.65em;
}
.weather .forecast .day {
font-size: 2em;
}
.weather table {
font-size: 2em;
}
.region.upper.third {
top: calc(33% - 60px);
}
.region.middle.center {
top: calc(65% - 120px);
}
.region.bottom.left {
max-height: 600px;
overflow: hidden;
}
.region.bottom.right {
max-height: 600px;
overflow: hidden;
}
.calendar .module-header {
font-size: 2em;
}
.calendar .title {
font-size: 1.5em;
}
.calendar .time {
font-size: 1.5em;
}
.calendar .symbol {
font-size: 1em;
}
.calendar .dateheader {
font-size: 1.5em;
}
.MMM-Todoist2 .module-header {
font-size: 2em;
}
.MMM-Todoist2 .title {
font-size: 1.5em;
}
.MMM-Todoist2 .dueDate {
font-size: 1.5em;
}
.MMM-Todoist2 .projectname {
font-size: 1.5em;
}
vi ~/MagicMirror/config/config.js
sudo reboot
When connected via an SSH session, use the following steps to capture a screenshot of the MagicMirror display. This is useful for adjusting font sizes and panel positioning without having to physically view the mirror each time.
Install grim — a screenshot utility for Wayland:
sudo apt install grim
Capture the screen. The WAYLAND_DISPLAY environment variable must be set to target the correct Wayland session:
WAYLAND_DISPLAY=wayland-0 grim ~/screenshot.png
The screenshot is saved to your home directory as screenshot.png. Open an SFTP session on your desktop machine to download a copy for analysis.
Note: grim requires a running Wayland compositor. If MagicMirror has not yet started the Wayland session is not active and the command will return a "cannot connect to wayland display" error. Make sure MagicMirror is running before capturing.
Use the following scripts combined with cron to automatically turn the monitor on and off on a daily schedule — useful for conserving power overnight without interrupting MagicMirror or any background processes.
Step 1 - Create the "Screen Off" script
Run this command in your terminal to create ~/screen_off.sh:
cat << 'EOF' > ~/screen_off.sh
#!/bin/bash
export XDG_RUNTIME_DIR=/run/user/1000
export WAYLAND_DISPLAY=wayland-0
/usr/bin/wlr-randr --output HDMI-A-1 --off
EOF
Step 2 - Create the "Screen On" script
Run this command in your terminal to create ~/screen_on.sh:
cat << 'EOF' > ~/screen_on.sh
#!/bin/bash
export XDG_RUNTIME_DIR=/run/user/1000
export WAYLAND_DISPLAY=wayland-0
/usr/bin/wlr-randr --output HDMI-A-1 --on
EOF
Step 3 - Make both scripts executable
chmod +x ~/screen_off.sh ~/screen_on.sh
Step 4 - Test the scripts manually
~/screen_off.sh
(Your monitor should go black / enter standby immediately.)
~/screen_on.sh
(Your monitor should power back on and display MagicMirror.)
Step 5 - Automate on a daily schedule (cron)
crontab -e
(If prompted to select an editor, choose 1 for nano.)
# Turn monitor OFF every night at 11:00 PM
0 23 * * * /home/pindari/screen_off.sh
# Turn monitor ON every morning at 7:00 AM
0 7 * * * /home/pindari/screen_on.sh
Your Raspberry Pi will now automatically sleep and wake the monitor at your scheduled times without interrupting any background processes or corrupting the system.