Searching on the internet I have seen made fancy smart mirror projects based on Magic Mirror open-source project; so I decided to build my own mirror project for my hall.

MagicMirror² is an open-source project based on node.js and electron providing a powerful API and also a huge community providing plugins for integrating many different kinds of information on the mirror. The project page is located at https://magicmirror.builders/ and the installation is simple as executing the following command:

> curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
> sudo apt install -y nodejs
> git clone https://github.com/MichMich/MagicMirror
> cd MagicMirror/
> npm install
> cp config/config.js.sample config/config.js
> npm run start

Although the project is mainly designed for Raspberry Pi platforms and also most plugins are designed to use Rpi hardware components, for my project I used an Orange Pi Win board that I already had available – sitting idle for a long time. In any case, I strongly recommend using a Rpi family board for your projects.

The many hardware components for the project are the following:

  1. Embedded signle board computer, I used the oPiWin for this project.
  2. 22″ LCD monitor.
  3. HDMI cable.
  4. Photo frame.
  5. Mirror wrap sticker, you can also use two-way mirror but it quiote more expensive.
  6. Motion sensor, use for wakeing up the monitor.
  7. Temperature humidity sensor, used for monitoring the room temperature. I used an AM3230 like I2C sensor.
  8. Few wood pieces for mounting the monitor on the photo frame.

The first step was to wrap the vinyl mirror on the photo frame glass and mount the monitor to the photo frame:

For the OPI I used an Armbian build and also added a few drivers for the sensors and power management – communicating with MQTT with the Magic Mirror. This step is most probably not needed if you go with RPI that is fully supported by the Magic Mirror project.

Then we started building the board, adding the required plugins for our application. You can find many lists with the available third-party/open-source plugins on the internet like the following one: https://github.com/MichMich/MagicMirror/wiki/3rd-Party-Modules

For my application I sed the following plug-ins:

NameDescription
calendar_monthlyDisplay monthly calendar
MMM-GoogleMapsTrafficDisplay local traffic map
MMM-JastStock exchange info, based on yahoo finance
MMM-OpenWeatherForecastCurrent weather information and prediction
MMM-XmasChristmas lights
MMM-GooglePhotosSlideshow from google photos
MMM-MQTTMQTT API for communicating with on board sensors
MMM-RTSPStreamDisplay video snaphosts from surveillance cameras

Plugins integraded on my Mirror:

Plugins are installed by just cloning the plugin source in the modules folder and running npm install for insalling all the dependencies. The whole board configuration in based on the config/config.jss file which for my project it like like the following:

/* Magic Mirror Config Sample
 *
 * By Michael Teeuw https://michaelteeuw.nl
 * MIT Licensed.
 *
 * For more information on how you can configure this file
 * see https://docs.magicmirror.builders/getting-started/configuration.html#general
 * and https://docs.magicmirror.builders/modules/configuration.html
 */
let config = {
    address: "0.0.0.0",
    port: 8080,
    basePath: "/",
    ipWhitelist: [],
    useHttps: false,
    httpsPrivateKey: "",
    httpsCertificate: "",
    language: "el",
    logLevel: ["INFO", "LOG", "WARN", "ERROR"],
    timeFormat: 24,
    units: "metric",
    serverOnly:  "local" ,

	modules: [
		{
			module: "alert",
		},
		{
			module: "updatenotification",
			position: "top_bar"
		},
		{
			module: "clock",
			position: "top_left"
		},
		{
            		module: "calendar",
            		header: "Public holidays",
            		position: "top_left",
            		config: {
                		calendars: [
                    		{
                        		symbol: "calendar-check",
                        		url: "webcal://www.calendarlabs.com/ical-calendar/ics/47/GR_Holidays.ics"
                    		},
                                {
                                        symbol: "ioko",
                                        url: "https://calendar.google.com/calendar/u/0?cid=axxxxx",
                                        auth: {
                                                user: 'xxx.xxx@xxx.com',
                                                pass: 'xxxx',
                                                method: 'basic'
                                        }
                                }
				]
            		}
        	},
		{
			module: 'calendar_monthly',
			position: 'top_left',
			config: {

			}
		},
		{
	 		module: 'MMM-Xmas',
	 		position: 'fullscreen_above'
  		},
		{
			module: "MMM-GooglePhotos",
  			position: "bottom_right",
  			config: {
				albums: [""],
				updateInterval: 20000,
				sort: "random",
				uploadAlbum: null,
				condition: {
					fromDate: null, 
					toDate: null, 
					minWidth: null, 
					maxWidth: null, 
					minHeight: null,
					maxHeight: null,
					minWHRatio: null,
					maxWHRatio: null,					
				},
				showWidth: 480,
				showHeight: 480,
				autoInfoPosition: true,
				timeFormat: "DD/MM/YYYY",
  			}
		},
		{
            		module: 'MMM-MQTT',
            		position: 'bottom_left',
            		header: 'Lounch',
            		config: {
                		logging: false,
                		useWildcards: false,
                		mqttServers: [
                    		{
                        		address: 'localhost',
                        		port: '1883',
                        		user: '',
                        		password: '',  
                        		subscriptions: [
                            		{
                                		topic: 'home/lounch/temperature',
                                		label: 'Temperature',
                                		suffix: '°C',
                                		decimals: 2,
                                		sortOrder: 10,                    		jsonpointer: '/temperature',
                                		maxAgeSeconds: 3600,
                                		colors: [ 
                                    		{ upTo: -10, value: "blue", label: "blue", suffix: "blue" },
                                    		{ upTo: 0, value: "#00ccff", label: "#00ccff", suffix: "#00ccff" },
                                    		{ upTo: 10, value: "yellow"},
                                    		{ upTo: 25, label: "green", suffix: "green" },
                                    		{ upTo: 100, label: "red" }, 
                                		],
                            		},
                            		{
                                		topic: 'home/lounch/temperature',
                                		label: 'Humidity',
                                		suffix: '%',
                                		decimals: 0,
                                		sortOrder: 20,
                                		jsonpointer: '/humidity',
                                		maxAgeSeconds: 3600
                            		},
                            		{
                                		topic: 'home/lounch/motion',
                                		label: 'Motion',
                                		jsonpointer: '/motion',
                                			conversions: [
                                    			{ from: 'true', to: 'Active' },
                                    			{ from: 'false', to: 'None' }
                               			]
                            		}
                        	]
                    		}
                	],
            		}
	        },
        	{
            		module: "MMM-RTSPStream",
            		position: "middle_center",
            		config: {
                		autoStart: true,
                		rotateStreams: true,
                		rotateStreamTimeout: 20,
				moduleWidth: 320,
                		moduleHeight: 220,
                                moduleOffset: { left: 0, top: 0 },
                		localPlayer: 'mplayer',
                		remotePlayer: 'none',
                		showSnapWhenPaused: true,
                		remoteSnaps: false,
                		shutdownDelay: 0,
                		stream1: {
                    			name: 'Entry',
                    			url: 'rtsp://192.168.1.2:8100/Streaming/Channels/302',
                    			frameRate: 8,
                    			snapshotType: 'url',
                    			snapshotRefresh: 8, 
					width: 320,
                    			height: 240,
                		},
				stream2: {
                                        name: 'Backyard',
                                        url: 'rtsp://192.168.1.2:8100/Streaming/Channels/202',
                                        frameRate: 8,
                                        snapshotType: 'url',
                                        snapshotRefresh: 8,
                                        width: 352,
                                        height: 288,
                                },
                                stream3: {
                                        name: 'Melisi',
                                        url: 'rtsp://178.134.109.10:8111/Streaming/Channels/102',
                                        frameRate: 8,
                                        snapshotType: 'url',
                                        snapshotRefresh: 8,
                                        width: 360,
                                        height: 240,
                                },

			}
		},
  		{
    			module: "MMM-OpenWeatherForecast",
    			position: "top_right",
    			header: "Forecast",
    			config: {
      				apikey: "xxxxxx",
				language: "el",
				forecastLayout: "table",
				showSummary: "true",
				animateMainIconOnly: true,
				animatedIconStartDelay: 10,
				showHourlyForecast: true,
				showDailyForecast: true,
      				latitude: 38.0735911,            //number works here
      				longitude: 23.7751825          //so does a string
    			}
		},
                {
                        module: 'MMM-GoogleMapsTraffic',
                        position: 'bottom_left',
                        config: {
                                key: 'AIxxxxxx',
			 	lat: 38.01,
				lng: 23.73,
                                height: '350px',
                                width: '350px',
				zoom: 11,
                                styledMapType: "dark", 
				updateInterval: 1800000,
                                disableDefaultUI: true,
                                backgroundColor: 'hsla(0, 0%, 0%, 0)',
                                markers: [
                                ],
                        }
                },
		{
                        module: "MMM-Jast",
                        position: "bottom_right",
                        config: {
                                maxWidth: "100%",
                                updateIntervalInSeconds: 600,
                                fadeSpeedInSeconds: 3.0,
                                scroll: "vertical",
                                useGrouping: false,
                                currencyStyle: "symbol",
                                lastUpdateFormat: "HH:mm:ss",
                                showColors: true,
                                showCurrency: true,
                                showChangePercent: true,
                                showChangeValue: false,
                                showChangeValueCurrency: false,
                                showLastUpdate: true,
                                showPortfolioValue: false,
                                showPortfolioGrowthPercent: false,
                                showPortfolioGrowth: false,
                                numberDecimalsValues: 2,
                                numberDecimalsPercentages: 2,
                                virtualHorizontalMultiplier: 2,
                                stocks: [
                                        { name: "ETE", symbol: "ETE.AT"},
                                        { name: "U-BLOX", symbol: "UBXN.SW"}
                                        ]
                                }
                },
		{
                        module: "newsfeed",
                        position: "bottom_bar",
                        config: {
                                feeds: [
                                {
                                        title: "NewsBeast",
                                        url: "https://www.newsbeast.gr/greece/feed"
                                },
				{
				        title: "Εορτολόγιο",
					url: "https://www.greeknamedays.gr/tools/eortologiorssfeed/index.php?langid=gr"
				}
                                ],
                                showSourceTitle: true,
                                showPublishDate: true,
                                broadcastNewsFeeds: true,
                                broadcastNewsUpdates: true
                        }
                }
	]
};

/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;}

Next steps are to integrate the MagicMirrow screen with my home assistant and add an automation API, like google Assistance for controlling my devices.

By ioko

Leave a Reply