├── pix ├── 1.JPG ├── 1.gif ├── 11.JPG ├── 2.JPG ├── cc.JPG └── phases.png ├── node_helper.js ├── LICENSE ├── README.md ├── MMM-SunRiseSet.css └── MMM-SunRiseSet.js /pix/1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mykle1/MMM-SunRiseSet/HEAD/pix/1.JPG -------------------------------------------------------------------------------- /pix/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mykle1/MMM-SunRiseSet/HEAD/pix/1.gif -------------------------------------------------------------------------------- /pix/11.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mykle1/MMM-SunRiseSet/HEAD/pix/11.JPG -------------------------------------------------------------------------------- /pix/2.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mykle1/MMM-SunRiseSet/HEAD/pix/2.JPG -------------------------------------------------------------------------------- /pix/cc.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mykle1/MMM-SunRiseSet/HEAD/pix/cc.JPG -------------------------------------------------------------------------------- /pix/phases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mykle1/MMM-SunRiseSet/HEAD/pix/phases.png -------------------------------------------------------------------------------- /node_helper.js: -------------------------------------------------------------------------------- 1 | /* Magic Mirror 2 | * Module: MMM-SunRiseSet 3 | * 4 | * By Mykle1 5 | * 6 | */ 7 | const NodeHelper = require('node_helper'); 8 | const request = require('request'); 9 | 10 | 11 | 12 | module.exports = NodeHelper.create({ 13 | 14 | start: function() { 15 | console.log("Starting node_helper for: " + this.name); 16 | }, 17 | 18 | getSunRiseSet: function(url) { 19 | request({ 20 | url: url, 21 | method: 'GET' 22 | }, (error, response, body) => { 23 | if (!error && response.statusCode == 200) { 24 | var result = JSON.parse(body).results; 25 | // console.log(response.statusCode + result); // for checking 26 | this.sendSocketNotification('SUNRISESET_RESULT', result); 27 | } 28 | }); 29 | }, 30 | 31 | socketNotificationReceived: function(notification, payload) { 32 | if (notification === 'GET_SUNRISESET') { 33 | this.getSunRiseSet(payload); 34 | } 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mykle 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## MMM-SunRiseSet 2 | 3 | More information than you ever wanted to know about the rising and setting of the sun. 4 | 5 | ## The information 6 | 7 | * Sunrise: The actual time the rising sun breaches the horizon. 8 | * Sunset: The actual time the sun falls completely below the horizon. 9 | * Solar Noon: The actual time when the sun is at its highest altitude in the sky. 10 | * Day Length: The length of time between sunrise and sunset. 11 | * Civil Twilight: The geometric center of the Sun is at most 6 degrees below the horizon. 12 | * Nautical Twilight: The geometric center of the Sun is between 6 and 12 degrees below the horizon. 13 | * Astronomical Twilight: The geometric center of the Sun is between 12 and 18 degrees below the horizon. 14 | 15 | ## Examples with static graph 16 | 17 | * MMM-Lunartic in top_left region 18 | 19 | ![](pix/1.JPG) 20 | 21 | * MMM-SunRiseSet in bottom_left region 22 | 23 | ## Example animation instead of static graph 24 | 25 | ![](pix/1.gif), ![](pix/11.JPG) 26 | * Obviously, day or night will move much more slowly 27 | 28 | ## Or animated earth image 29 | * Updates to show daylight approaching or night approaching 30 | 31 | ![](pix/cc.JPG) 32 | 33 | * Config option to show just animated world or map without the data 34 | 35 | Annotated .css file included for aligning and coloring text. 36 | css file also used to display as little or as much data as you like. 37 | 38 | ## Installation 39 | 40 | * `git clone https://github.com/mykle1/MMM-SunRiseSet` into the `~/MagicMirror/modules` directory. 41 | 42 | * No API key needed! No dependencies needed! No kidding! 43 | 44 | 45 | ## Config.js entry and options 46 | 47 | { 48 | disabled: false, 49 | module: "MMM-SunRiseSet", 50 | position: "bottom_left", 51 | config: { 52 | lat: "41.111111", // Your latitude (for the data) 53 | lng: "-75.111111", // Your longitude (for the data) 54 | image: "map", // "world" (animation), "map" (animation), "static" (graph) 55 | imageOnly: "no", // no = all data, yes = only animated world or map 56 | dayOrNight: "night", // "night" approaching, "day" approaching (imageOnly: must be "yes", image: must be "world") 57 | timeFormat: "h:mm a", // Examples: "h:mm a" or "H:mm" 58 | useHeader: false, // true if you want a header 59 | header: "Header", // useHeader must be true 60 | maxWidth: "515px", 61 | } 62 | }, 63 | 64 | 65 | ## Thanks go to SpaceCowboysDude for UTC and moment advice 66 | ## Thanks also to Sean and Strawberry for updateDOM guidance 67 | -------------------------------------------------------------------------------- /MMM-SunRiseSet.css: -------------------------------------------------------------------------------- 1 | /* Copy and paste any or all of these css entries into your custom.css file inside your 2 | * css folder. The functions are annotated to make things as easy as possible for you. 3 | * Use any colors you want! Go to - "http://htmlcolorcodes.com/color-picker/" 4 | * Pick your color, copy and paste the HEX number. Example - #62FF00 = bright green. 5 | */ 6 | 7 | .MMM-SunRiseSet .header { 8 | color: white; /* Header color. Default is white. */ 9 | } 10 | 11 | .MMM-SunRiseSet .img { 12 | /* display: none; uncomment if you don't want animated image */ 13 | width: 100%; /* adjust size of image. Retains aspet ratio */ 14 | margin-left: 0px; /* Precisely align image with these */ 15 | margin-right: 0px; /* world = Adjust for different regions and images */ 16 | margin-top: 5px; /* world = -45 Adjust for different regions and images */ 17 | margin-bottom: -5px; /* world = -55 Adjust for different regions and images */ 18 | float: center; 19 | } 20 | 21 | 22 | .MMM-SunRiseSet .sunrise { 23 | color: white; /* sunrise color. Default is white. */ 24 | text-align: center; 25 | } 26 | 27 | .MMM-SunRiseSet .sunset { 28 | color: white; /* sunset color. Default is white. */ 29 | display: none; 30 | } 31 | 32 | .MMM-SunRiseSet .solar_noon { 33 | color: white; /* solar_noon color. Default is white. */ 34 | display: none; 35 | } 36 | 37 | .MMM-SunRiseSet .day_length { 38 | color: white; /* day_length color. Default is white. */ 39 | text-align: center; 40 | } 41 | 42 | .MMM-SunRiseSet .civil_twilight_begin { 43 | color: white; /* civil_twilight_begin color. Default is white. */ 44 | display: none; 45 | } 46 | 47 | .MMM-SunRiseSet .civil_twilight_end { 48 | color: white; /* civil_twilight_end color. Default is white. */ 49 | display: none; 50 | } 51 | 52 | .MMM-SunRiseSet .nautical_twilight_begin { 53 | color: white; /* nautical_twilight_begin color. Default is white. */ 54 | display: none; 55 | } 56 | 57 | .MMM-SunRiseSet .nautical_twilight_end { 58 | color: white; /* nautical_twilight_end color. Default is white. */ 59 | display: none; 60 | } 61 | 62 | .MMM-SunRiseSet .astronomical_twilight_begin { 63 | color: white; /* astronomical_twilight_begin color. Default is white. */ 64 | display: none; 65 | } 66 | 67 | .MMM-SunRiseSet .astronomical_twilight_end { 68 | color: white; /* astronomical_twilight_end color. Default is white. */ 69 | display: none; 70 | } -------------------------------------------------------------------------------- /MMM-SunRiseSet.js: -------------------------------------------------------------------------------- 1 | /* Magic Mirror 2 | * Module: MMM-SunRiseSet 3 | * 4 | * By Mykle1 5 | * MIT License 6 | */ 7 | Module.register("MMM-SunRiseSet", { 8 | 9 | // Module config defaults. 10 | defaults: { 11 | lat: "36.7201600", // latitude 12 | lng: "-4.4203400", // longitude 13 | image: "world", // world, map or static (for graph) 14 | imageOnly: "no", // no = all data, yes = only animated image choice 15 | dayOrNight: "night", // "night" approaching, "day" approaching (imageOnly must be "yes") 16 | timeFormat: "", 17 | useHeader: false, // true if you want a header 18 | header: "On to the heart of the sunrise", // Any text you want. useHeader must be true 19 | maxWidth: "300px", 20 | animationSpeed: 3000, 21 | initialLoadDelay: 1250, 22 | retryDelay: 2500, 23 | updateInterval: 5 * 60 * 1000, // 5 minutes 24 | 25 | }, 26 | 27 | getStyles: function() { 28 | return ["MMM-SunRiseSet.css"]; 29 | }, 30 | 31 | getScripts: function() { 32 | return ["moment.js"]; 33 | }, 34 | 35 | start: function() { 36 | Log.info("Starting module: " + this.name); 37 | 38 | requiresVersion: "2.1.0", 39 | 40 | // Set locale. 41 | this.url = "https://api.sunrise-sunset.org/json?lat=" + this.config.lat + "&lng=" + this.config.lng + "&date=today&formatted=0"; 42 | this.SunRiseSet = {}; 43 | this.scheduleUpdate(); 44 | }, 45 | 46 | getDom: function() { 47 | 48 | var wrapper = document.createElement("div"); 49 | wrapper.className = "wrapper"; 50 | wrapper.style.maxWidth = this.config.maxWidth; 51 | 52 | if (!this.loaded) { 53 | wrapper.innerHTML = "On to the heart of the sunrise"; 54 | wrapper.classList.add("bright", "light", "small"); 55 | return wrapper; 56 | } 57 | 58 | if (this.config.useHeader != false) { 59 | var header = document.createElement("header"); 60 | header.classList.add("xsmall", "bright", "light"); 61 | header.innerHTML = this.config.header; 62 | wrapper.appendChild(header); 63 | } 64 | 65 | ///////////// First - IF you want all the data and the image /////////////////// 66 | 67 | if (this.config.imageOnly != "yes") { 68 | 69 | var SunRiseSet = this.SunRiseSet; 70 | var lat = this.config.lat; // latitude 71 | var lng = this.config.lng; // longitude 72 | 73 | // if(!this.once) 74 | // { 75 | // this.once=true; 76 | // MM.getModules().enumerate((module) => { 77 | // Log.log("module="+module.name); 78 | // }) 79 | // } 80 | 81 | // var top = document.createElement("div"); 82 | // top.classList.add("list-row"); 83 | 84 | 85 | // choice of image // timestamp in url forces DOM to update @Sean & @ Strawberry 86 | var pic = document.createElement("div"); 87 | var img = document.createElement("img"); 88 | img.classList.add("img"); 89 | if (this.config.image == "static"){ 90 | img.src = "modules/MMM-SunRiseSet/pix/phases.png"; 91 | pic.appendChild(img); 92 | wrapper.appendChild(pic); 93 | } else if (this.config.image == "map") { 94 | var getTimeStamp = new Date().getTime(); // @Sean & @ Strawberry 95 | img.src = "http://api.usno.navy.mil/imagery/earth.png?date=today&seed=" + getTimeStamp; // 96 | // console.log(img.src); 97 | pic.appendChild(img); 98 | wrapper.appendChild(pic); 99 | } else { 100 | var getTimeStamp = new Date().getTime(); // @Sean & @ Strawberry 101 | img.src = "http://api.usno.navy.mil/imagery/earth.png?view=rise&seed=" + getTimeStamp; // 102 | pic.appendChild(img); 103 | wrapper.appendChild(pic); 104 | 105 | } 106 | 107 | 108 | // sunrise set to local time using moment 109 | var sunrise = document.createElement("div"); 110 | sunrise.classList.add("small", "bright", "sunrise"); 111 | sunrise.innerHTML = "Sunrise is at " + moment(SunRiseSet.sunrise).local().format(this.config.timeFormat) + "     " 112 | + "Sunset is at " + moment(SunRiseSet.sunset).local().format(this.config.timeFormat); // "h:mm a" 113 | wrapper.appendChild(sunrise); 114 | 115 | 116 | // // sunset set to local time using moment 117 | // var sunset = document.createElement("div"); 118 | // sunset.classList.add("xsmall", "bright", "sunset"); 119 | // sunset.innerHTML = "Sunset is at " + moment(SunRiseSet.sunset).local().format("h:mm a"); 120 | // wrapper.appendChild(sunset); 121 | 122 | 123 | // solar noon set to local time using moment 124 | var solar_noon = document.createElement("div"); 125 | solar_noon.classList.add("xsmall", "bright", "solar_noon"); 126 | solar_noon.innerHTML = "Solar noon is at " + moment(SunRiseSet.solar_noon).local().format(this.config.timeFormat); 127 | wrapper.appendChild(solar_noon); 128 | 129 | 130 | // convert utc day_length to human readable time 131 | var date = new Date(null); 132 | date.setSeconds(SunRiseSet.day_length); // specify value for SECONDS here 133 | var result = date.toISOString().substr(11, 8); 134 | // length of day 135 | var day_length = document.createElement("div"); 136 | day_length.classList.add("xsmall", "bright", "day_length"); 137 | day_length.innerHTML = "Length of day is " + result; 138 | wrapper.appendChild(day_length); 139 | 140 | 141 | // civil twilight begins set to local time using moment 142 | var civil_twilight_begin = document.createElement("div"); 143 | civil_twilight_begin.classList.add("xsmall", "bright", "civil_twilight_begin"); 144 | civil_twilight_begin.innerHTML = "Civil twilight begins at " + moment(SunRiseSet.civil_twilight_begin).local().format(this.config.timeFormat); 145 | wrapper.appendChild(civil_twilight_begin); 146 | 147 | 148 | // civil twilight ends set to local time using moment 149 | var civil_twilight_end = document.createElement("div"); 150 | civil_twilight_end.classList.add("xsmall", "bright", "civil_twilight_end"); 151 | civil_twilight_end.innerHTML = "Civil twilight ends at " + moment(SunRiseSet.civil_twilight_end).local().format(this.config.timeFormat); 152 | wrapper.appendChild(civil_twilight_end); 153 | 154 | 155 | // nautical_twilight_begin set to local time using moment 156 | var nautical_twilight_begin = document.createElement("div"); 157 | nautical_twilight_begin.classList.add("xsmall", "bright", "nautical_twilight_begin"); 158 | nautical_twilight_begin.innerHTML = "Nautical twilight begins at " + moment(SunRiseSet.nautical_twilight_begin).local().format(this.config.timeFormat); 159 | wrapper.appendChild(nautical_twilight_begin); 160 | 161 | 162 | // nautical_twilight_end set to local time using moment 163 | var nautical_twilight_end = document.createElement("div"); 164 | nautical_twilight_end.classList.add("xsmall", "bright", "nautical_twilight_end"); 165 | nautical_twilight_end.innerHTML = "Nautical twilight ends at " + moment(SunRiseSet.nautical_twilight_end).local().format(this.config.timeFormat); 166 | wrapper.appendChild(nautical_twilight_end); 167 | 168 | 169 | // astronomical_twilight_begin set to local time using moment 170 | var astronomical_twilight_begin = document.createElement("div"); 171 | astronomical_twilight_begin.classList.add("xsmall", "bright", "astronomical_twilight_begin"); 172 | astronomical_twilight_begin.innerHTML = "Astronomical twilight begins at " + moment(SunRiseSet.astronomical_twilight_begin).local().format(this.config.timeFormat); 173 | wrapper.appendChild(astronomical_twilight_begin); 174 | 175 | 176 | // astronomical_twilight_end set to local time using moment 177 | var astronomical_twilight_end = document.createElement("div"); 178 | astronomical_twilight_end.classList.add("xsmall", "bright", "astronomical_twilight_end"); 179 | astronomical_twilight_end.innerHTML = "Astronomical twilight ends at " + moment(SunRiseSet.astronomical_twilight_end).local().format(this.config.timeFormat); 180 | wrapper.appendChild(astronomical_twilight_end); 181 | 182 | 183 | 184 | 185 | ////////////////// ELSE - Only the world map image ////////////// 186 | 187 | } else { 188 | 189 | // var top = document.createElement("div"); 190 | // top.classList.add("list-row"); 191 | 192 | var pic = document.createElement("div"); 193 | var img = document.createElement("img"); 194 | img.classList.add("img"); 195 | if (this.config.image == "static"){ 196 | img.src = "modules/MMM-SunRiseSet/pix/phases.png"; 197 | pic.appendChild(img); 198 | wrapper.appendChild(pic); 199 | } else if (this.config.image == "map") { 200 | var getTimeStamp = new Date().getTime(); // @Sean & @ Strawberry 201 | img.src = "http://api.usno.navy.mil/imagery/earth.png?date=today&seed=" + getTimeStamp; // 202 | // console.log(img.src); 203 | pic.appendChild(img); 204 | wrapper.appendChild(pic); 205 | } else { 206 | var getTimeStamp = new Date().getTime(); // @Sean & @ Strawberry 207 | if (this.config.dayOrNight == "day") { 208 | img.src = "http://api.usno.navy.mil/imagery/earth.png?view=rise&seed=" + getTimeStamp; // 209 | pic.appendChild(img); 210 | wrapper.appendChild(pic); 211 | 212 | } else { 213 | var getTimeStamp = new Date().getTime(); // @Sean & @ Strawberry 214 | img.src = "http://api.usno.navy.mil/imagery/earth.png?view=set&seed=" + getTimeStamp; 215 | pic.appendChild(img); 216 | wrapper.appendChild(pic); 217 | 218 | } 219 | 220 | } 221 | 222 | 223 | } // closes top if/else statement 224 | 225 | 226 | return wrapper; 227 | }, 228 | 229 | 230 | ///// Add this function to the modules you want to control with voice ////// 231 | 232 | notificationReceived: function(notification, payload) { 233 | if (notification === 'HIDE_SUNRISE') { 234 | this.hide(1000); 235 | } else if (notification === 'SHOW_SUNRISE') { 236 | this.show(1000); 237 | } 238 | 239 | }, 240 | 241 | 242 | processSunRiseSet: function(data) { 243 | this.SunRiseSet = data; 244 | this.loaded = true; 245 | }, 246 | 247 | scheduleUpdate: function() { 248 | setInterval(() => { 249 | this.getSunRiseSet(); 250 | }, this.config.updateInterval); 251 | this.getSunRiseSet(this.config.initialLoadDelay); 252 | }, 253 | 254 | getSunRiseSet: function() { 255 | this.sendSocketNotification('GET_SUNRISESET', this.url); 256 | }, 257 | 258 | socketNotificationReceived: function(notification, payload) { 259 | if (notification === "SUNRISESET_RESULT") { 260 | this.processSunRiseSet(payload); 261 | 262 | this.updateDom(this.config.animationSpeed); 263 | } 264 | this.updateDom(this.config.initialLoadDelay); 265 | }, 266 | }); 267 | --------------------------------------------------------------------------------