├── exampleImages ├── test.txt ├── matrix.jpg ├── office.gif └── jurassicpark.jpg ├── imageslideshow.css ├── LICENSE ├── .gitignore ├── README.md ├── node_helper.js └── MMM-ImageSlideshow.js /exampleImages/test.txt: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /exampleImages/matrix.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdamMoses-GitHub/MMM-ImageSlideshow/HEAD/exampleImages/matrix.jpg -------------------------------------------------------------------------------- /exampleImages/office.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdamMoses-GitHub/MMM-ImageSlideshow/HEAD/exampleImages/office.gif -------------------------------------------------------------------------------- /exampleImages/jurassicpark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdamMoses-GitHub/MMM-ImageSlideshow/HEAD/exampleImages/jurassicpark.jpg -------------------------------------------------------------------------------- /imageslideshow.css: -------------------------------------------------------------------------------- 1 | img.desaturate { 2 | -webkit-filter: grayscale(1); 3 | -webkit-filter: grayscale(100%); 4 | filter: gray; 5 | filter: grayscale(100%); 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Adam Moses 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Module: Image Slideshow 2 | The `MMM-ImageSlideshow` module is designed to display images, one at a time on a fixed interval, from one or many directories. These images can be shown in order or at random, one directory at a time or all at time. The image heights and widths can be fixed, and the images can be made to be shown in grayscale. 3 | 4 | ## Dependencies / Requirements 5 | 6 | This module requires no special dependencies. The only requirement is that the image directories you path to are fixed paths accessible to the Magic Mirror instance. 7 | 8 | ## Operation 9 | 10 | This module will take in a list of directory paths, one or more, containing image files. The module will display those images in either alphabetical or random order, across either each path one at time or across all the paths at once. Once all the images have been shown, it will loop back and start again. 11 | 12 | Extra configurations include setting the amount of time an image is shown for, rendering the images in grayscale, selecting which file extensions are valid, and using a fixed image height and/or width. 13 | 14 | 15 | ## Using the module 16 | 17 | To use this module, add it to the modules array in the `config/config.js` file: 18 | ````javascript 19 | modules: [ 20 | { 21 | module: 'MMM-ImageSlideshow', 22 | position: 'bottom_left', 23 | config: { 24 | imagePaths: ['modules/MMM-ImageSlideshow/example1'] 25 | } 26 | } 27 | ] 28 | ```` 29 | 30 | ## Configuration options 31 | 32 | The following properties can be configured: 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 49 | 50 | 51 | 52 | 57 | 58 | 59 | 60 | 65 | 66 | 67 | 68 | 73 | 74 | 75 | 76 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 97 | 98 | 99 | 100 | 105 | 106 | 107 | 108 | 113 | 114 | 115 |
OptionDescription
imagePathsArray value containing strings. Each string should be a path to a directory where image files can be found.
46 |
Example: ['modules/MMM-ImageSlideshow/example1'] 47 |
This value is REQUIRED 48 |
slideshowSpeedInteger value, the length of time to show one image before switching to the next, in milliseconds.
53 |
Example: 6000 54 |
Default value: 10000 55 |
This value is OPTIONAL 56 |
delayUntilRestartInteger value, the length of time to restart the slideshow after the last image has been shown, in milliseconds. The module will go blank will waits to restart. This value defaults to zero, meaning no delay until restarting.
61 |
Example: 6000 62 |
Default value: 0 63 |
This value is OPTIONAL 64 |
fixedImageWidthInteger value, sets a fixed pixel width for all the images to be shown. If set to 0, the image's actual width is used.
69 |
Example: 250 70 |
Default value: 0 71 |
This value is OPTIONAL 72 |
fixedImageHeightInteger value, sets a fixed pixel height for all the images to be shown. If set to 0, the image's actual height is used.
77 |
Example: 300 78 |
Default value: 0 79 |
This value is OPTIONAL 80 |
randomizeImageOrderBoolean value, if true will randomize the order of the images, if false will use an alphabetical sorting by filename.
85 |
Example: true 86 |
Default value: false 87 |
This value is OPTIONAL 88 |
treatAllPathsAsOneBoolean value, if true will treat all the paths specified in imagePaths as one path. Otherwise, if false, images will only be shown from one path at a time in the order of imagePaths, until all the images in that path are exhausted, before continuing to the next path.
93 |
Example: true 94 |
Default value: false 95 |
This value is OPTIONAL 96 |
makeImagesGrayscaleBoolean value, if true images will be rendered in grayscale (i.e black and white) instead of color. If false they will be rendered as just they are without change.
101 |
Example: true 102 |
Default value: false 103 |
This value is OPTIONAL 104 |
validImageFileExtensionsString value, a list of image file extensions, seperated by commas, that should be included. Files found without one of the extensions will be ignored.
109 |
Example: 'png,jpg' 110 |
Default value: 'bmp,jpg,gif,png' 111 |
This value is OPTIONAL 112 |
116 | -------------------------------------------------------------------------------- /node_helper.js: -------------------------------------------------------------------------------- 1 | /* global Module */ 2 | 3 | /* node_helper.js 4 | * 5 | * Magic Mirror 6 | * Module: MMM-ImageSlideshow 7 | * 8 | * Magic Mirror By Michael Teeuw http://michaelteeuw.nl 9 | * MIT Licensed. 10 | * 11 | * Module MMM-ImageSlideshow By Adam Moses http://adammoses.com 12 | * MIT Licensed. 13 | */ 14 | 15 | // call in the required classes 16 | var NodeHelper = require("node_helper"); 17 | var FileSystemImageSlideshow = require("fs"); 18 | 19 | // the main module helper create 20 | module.exports = NodeHelper.create({ 21 | // subclass start method, clears the initial config array 22 | start: function() { 23 | this.moduleConfigs = []; 24 | }, 25 | // shuffles an array at random and returns it 26 | shuffleArray: function(array) { 27 | var currentIndex = array.length, temporaryValue, randomIndex; 28 | while (0 !== currentIndex) { 29 | randomIndex = Math.floor(Math.random() * currentIndex); 30 | currentIndex -= 1; 31 | temporaryValue = array[currentIndex]; 32 | array[currentIndex] = array[randomIndex]; 33 | array[randomIndex] = temporaryValue; 34 | } 35 | return array; 36 | }, 37 | // sort by filename attribute 38 | sortByFilename: function (a, b) { 39 | aL = a.filename.toLowerCase(); 40 | bL = b.filename.toLowerCase(); 41 | if (aL > bL) 42 | return 1; 43 | else 44 | return -1; 45 | }, 46 | // checks there's a valid image file extension 47 | checkValidImageFileExtension: function(filename, extensions) { 48 | var extList = extensions.split(','); 49 | for (var extIndex = 0; extIndex < extList.length; extIndex++) { 50 | if (filename.toLowerCase().endsWith(extList[extIndex])) 51 | return true; 52 | } 53 | return false; 54 | }, 55 | // gathers the image list 56 | gatherImageList: function(config) { 57 | var self = this; 58 | // create an empty main image list 59 | var imageList = []; 60 | // for each of the paths specified 61 | for (var pathIndex = 0; pathIndex < config.imagePaths.length; pathIndex++) { 62 | var currentPath = config.imagePaths[pathIndex]; 63 | var currentPathImageList = FileSystemImageSlideshow.readdirSync(path = currentPath); 64 | // for each file in the current path 65 | if (currentPathImageList.length > 0) { 66 | // create an empty list for images in the current path 67 | var currentImageList = []; 68 | // for each file 69 | for (var imageIndex = 0; imageIndex < currentPathImageList.length; imageIndex++) { 70 | // seperate into path and filename 71 | var currentImage = {path: currentPath, filename: currentPathImageList[imageIndex]}; 72 | // check if file has a valid image file extension 73 | var isValidImageFileExtension = this.checkValidImageFileExtension( 74 | currentImage.filename, 75 | config.validImageFileExtensions); 76 | // if file is valid, add it to the list 77 | if (isValidImageFileExtension) 78 | currentImageList.push(currentImage); 79 | } 80 | // if not set to combine all paths, do random or alphabetical sort 81 | if (!config.treatAllPathsAsOne) { 82 | if (config.randomizeImageOrder) 83 | currentImageList = this.shuffleArray(currentImageList); 84 | else 85 | currentImageList = currentImageList.sort(this.sortByFilename); 86 | } 87 | // add current list main list 88 | imageList = imageList.concat(currentImageList); 89 | } 90 | } 91 | // if set to combine all paths, sort all images randomly or alphabetically by filename 92 | if (config.treatAllPathsAsOne) { 93 | if (config.randomizeImageOrder) 94 | imageList = this.shuffleArray(imageList); 95 | else 96 | imageList = imageList.sort(this.sortByFilename); 97 | } 98 | // create a file image list combining paths and filenames 99 | var imageListComplete = []; 100 | for (var index = 0; index < imageList.length; index++) { 101 | imageListComplete.push(imageList[index].path + '/' + imageList[index].filename); 102 | } 103 | // return final list 104 | return imageListComplete; 105 | }, 106 | // subclass socketNotificationReceived, received notification from module 107 | socketNotificationReceived: function(notification, payload) { 108 | if (notification === "IMAGESLIDESHOW_REGISTER_CONFIG") { 109 | // add the current config to an array of all configs used by the helper 110 | this.moduleConfigs.push(payload); 111 | // this to self 112 | var self = this; 113 | // get the image list 114 | var imageList = this.gatherImageList(payload); 115 | // build the return payload 116 | var returnPayload = { identifier: payload.identifier, imageList: imageList }; 117 | // send the image list back 118 | self.sendSocketNotification('IMAGESLIDESHOW_FILELIST', returnPayload ); 119 | } 120 | }, 121 | }); 122 | 123 | //------------ end ------------- 124 | -------------------------------------------------------------------------------- /MMM-ImageSlideshow.js: -------------------------------------------------------------------------------- 1 | /* global Module */ 2 | 3 | /* MMM-ImageSlideshow.js 4 | * 5 | * Magic Mirror 6 | * Module: MMM-ImageSlideshow 7 | * 8 | * Magic Mirror By Michael Teeuw http://michaelteeuw.nl 9 | * MIT Licensed. 10 | * 11 | * Module MMM-ImageSlideshow By Adam Moses http://adammoses.com 12 | * MIT Licensed. 13 | */ 14 | 15 | Module.register("MMM-ImageSlideshow", { 16 | // Default module config. 17 | defaults: { 18 | // an array of strings, each is a path to a directory with images 19 | imagePaths: [ 'modules/MMM-ImageSlideshow/exampleImages' ], 20 | // the speed at which to switch between images, in milliseconds 21 | slideshowSpeed: 10 * 1000, 22 | // if zero do nothing, otherwise set width to a pixel value 23 | fixedImageWidth: 0, 24 | // if zero do nothing, otherwise set height to a pixel value 25 | fixedImageHeight: 0, 26 | // if true randomize image order, otherwise do alphabetical 27 | randomizeImageOrder: false, 28 | // if true combine all images in all the paths 29 | // if false each path with be viewed seperately in the order listed 30 | treatAllPathsAsOne: false, 31 | // if true, all images will be made grayscale, otherwise as they are 32 | makeImagesGrayscale: false, 33 | // list of valid file extensions, seperated by commas 34 | validImageFileExtensions: 'bmp,jpg,gif,png', 35 | // a delay timer after all images have been shown, to wait to restart (in ms) 36 | delayUntilRestart: 0, 37 | }, 38 | // load function 39 | start: function () { 40 | // add identifier to the config 41 | this.config.identifier = this.identifier; 42 | // ensure file extensions are lower case 43 | this.config.validImageFileExtensions = this.config.validImageFileExtensions.toLowerCase(); 44 | // set no error 45 | this.errorMessage = null; 46 | if (this.config.imagePaths.length == 0) { 47 | this.errorMessage = "MMM-ImageSlideshow: Missing required parameter." 48 | } 49 | else { 50 | // create an empty image list 51 | this.imageList = []; 52 | // set beginning image index to -1, as it will auto increment on start 53 | this.imageIndex = -1; 54 | // ask helper function to get the image list 55 | this.sendSocketNotification('IMAGESLIDESHOW_REGISTER_CONFIG', this.config); 56 | // do one update time to clear the html 57 | this.updateDom(); 58 | // set a blank timer 59 | this.interval = null; 60 | } 61 | }, 62 | // Define required scripts. 63 | getStyles: function() { 64 | // the css contains the make grayscale code 65 | return ["imageslideshow.css"]; 66 | }, 67 | // the socket handler 68 | socketNotificationReceived: function(notification, payload) { 69 | // if an update was received 70 | if (notification === "IMAGESLIDESHOW_FILELIST") { 71 | // check this is for this module based on the woeid 72 | if (payload.identifier === this.identifier) 73 | { 74 | // set the image list 75 | this.imageList = payload.imageList; 76 | // if image list actually contains images 77 | // set loaded flag to true and update dom 78 | if (this.imageList.length > 0) { 79 | this.loaded = true; 80 | this.updateDom(); 81 | // set the timer schedule to the slideshow speed 82 | var self = this; 83 | this.interval = setInterval(function() { 84 | self.updateDom(); 85 | }, this.config.slideshowSpeed); 86 | } 87 | } 88 | } 89 | }, 90 | // Override dom generator. 91 | getDom: function () { 92 | var wrapper = document.createElement("div"); 93 | // if an error, say so (currently no errors can occur) 94 | if (this.errorMessage != null) { 95 | wrapper.innerHTML = this.errorMessage; 96 | } 97 | // if no errors 98 | else { 99 | // if the image list has been loaded 100 | if (this.loaded === true) { 101 | // if was delayed until restart, reset index reset timer 102 | if (this.imageIndex == -2) { 103 | this.imageIndex = -1; 104 | clearInterval(this.interval); 105 | var self = this; 106 | this.interval = setInterval(function() { 107 | self.updateDom(0); 108 | }, this.config.slideshowSpeed); 109 | } 110 | // iterate the image list index 111 | this.imageIndex += 1; 112 | // set flag to show stuff 113 | var showSomething = true; 114 | // if exceeded the size of the list, go back to zero 115 | if (this.imageIndex == this.imageList.length) { 116 | // if delay after last image, set to wait 117 | if (this.config.delayUntilRestart > 0) { 118 | this.imageIndex = -2; 119 | wrapper.innerHTML = " "; 120 | showSomething = false; 121 | clearInterval(this.interval); 122 | var self = this; 123 | this.interval = setInterval(function() { 124 | self.updateDom(0); 125 | }, this.config.delayUntilRestart); 126 | } 127 | // if not reset index 128 | else 129 | this.imageIndex = 0; 130 | } 131 | if (showSomething) { 132 | // create the image dom bit 133 | var image = document.createElement("img"); 134 | // if set to make grayscale, flag the class set in the .css file 135 | if (this.config.makeImagesGrayscale) 136 | image.className = "desaturate"; 137 | // create an empty string 138 | var styleString = ''; 139 | // if width or height or non-zero, add them to the style string 140 | if (this.config.fixedImageWidth != 0) 141 | styleString += 'width:' + this.config.fixedImageWidth + 'px;'; 142 | if (this.config.fixedImageHeight != 0) 143 | styleString += 'height:' + this.config.fixedImageHeight + 'px;'; 144 | // if style string has antyhing, set it 145 | if (styleString != '') 146 | image.style = styleString; 147 | // set the image location 148 | image.src = encodeURI(this.imageList[this.imageIndex]); 149 | // ad the image to the dom 150 | wrapper.appendChild(image); 151 | } 152 | } 153 | else { 154 | // if no data loaded yet, empty html 155 | wrapper.innerHTML = " "; 156 | } 157 | } 158 | // return the dom 159 | return wrapper; 160 | } 161 | }); 162 | --------------------------------------------------------------------------------