├── 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 | | Option |
39 | Description |
40 |
41 |
42 |
43 |
44 | imagePaths |
45 | Array 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 | |
49 |
50 |
51 | slideshowSpeed |
52 | Integer 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 | |
57 |
58 |
59 | delayUntilRestart |
60 | Integer 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 | |
65 |
66 |
67 | fixedImageWidth |
68 | Integer 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 | |
73 |
74 |
75 | fixedImageHeight |
76 | Integer 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 | |
81 |
82 |
83 | randomizeImageOrder |
84 | Boolean 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 | |
89 |
90 |
91 | treatAllPathsAsOne |
92 | Boolean 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 | |
97 |
98 |
99 | makeImagesGrayscale |
100 | Boolean 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 | |
105 |
106 |
107 | validImageFileExtensions |
108 | String 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 | |
113 |
114 |
115 |
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 |
--------------------------------------------------------------------------------