Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MMM-DynamicWeather.ts 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841
  1. /* Magic Mirror
  2. * Module: MMM-DynamicWeather
  3. *
  4. * By Scott Lewis - https://github.com/scottcl88/MMM-DynamicWeather
  5. * MIT Licensed.
  6. *
  7. * A heavily configurable MagicMirror Module to display different animations based on current weather and show customized event effects.
  8. */
  9. declare var Log: Console;
  10. declare var Module: any;
  11. class Effect {
  12. constructor() {}
  13. id: number; //an id made up to use for sequential logic
  14. month: number;
  15. day: number;
  16. year: number = 0;
  17. images: string[];
  18. direction: string;
  19. size: number;
  20. particleCount: number;
  21. speedMax: number;
  22. speedMin: number;
  23. weatherCode: number;
  24. weatherCodeMin: number;
  25. weatherCodeMax: number;
  26. holiday: string;
  27. recurrence: string; //monthly, weekly, weekdays, weekends
  28. doDisplay: boolean = false;
  29. public getYear(): number {
  30. return this.year ? this.year : 0;
  31. }
  32. public getMonth(): number {
  33. return this.month ? this.month : 0;
  34. }
  35. public getDay(): number {
  36. return this.day ? this.day : 0;
  37. }
  38. public getSize(): number {
  39. return this.size ? this.size : 1;
  40. }
  41. public getParticleCount(): number {
  42. return this.particleCount ? this.particleCount : -1;
  43. }
  44. public getSpeedMax(): number {
  45. return this.speedMax ? this.speedMax : 100;
  46. }
  47. public getSpeedMin(): number {
  48. return this.speedMin ? this.speedMin : 50;
  49. }
  50. public getWeatherCode(): number {
  51. return this.weatherCode ? this.weatherCode : -99;
  52. }
  53. public getMinWeatherCode(): number {
  54. return this.weatherCodeMin ? this.weatherCodeMin : 99999;
  55. }
  56. public getMaxWeatherCode(): number {
  57. return this.weatherCodeMax ? this.weatherCodeMax : -99999;
  58. }
  59. public hasWeatherCode(): boolean {
  60. return (this.weatherCode && this.weatherCode > 0) || (this.weatherCodeMin && this.weatherCodeMin > 0) || (this.weatherCodeMax && this.weatherCodeMax > 0) ? true : false;
  61. }
  62. public hasHoliday(): boolean {
  63. return this.holiday && this.holiday.length > 0 ? true : false;
  64. }
  65. public clone(other: Effect) {
  66. this.id = other.id;
  67. this.day = other.day;
  68. this.month = other.month;
  69. this.year = other.year;
  70. this.images = other.images;
  71. this.direction = other.direction;
  72. this.size = other.size;
  73. this.particleCount = other.particleCount;
  74. this.speedMax = other.speedMax;
  75. this.speedMin = other.speedMin;
  76. this.weatherCode = other.weatherCode;
  77. this.weatherCodeMin = other.weatherCodeMin;
  78. this.weatherCodeMax = other.weatherCodeMax;
  79. this.holiday = other.holiday;
  80. this.recurrence = other.recurrence;
  81. this.doDisplay = other.doDisplay;
  82. }
  83. }
  84. Module.register("MMM-DynamicWeather", {
  85. defaults: {
  86. particleCount: 100,
  87. api_key: "",
  88. locationID: 0,
  89. lat: 0,
  90. lon: 0,
  91. weatherInterval: 600000, // Every 10 minutes,
  92. alwaysDisplay: "",
  93. zIndex: 99,
  94. opacity: 1,
  95. fadeDuration: 3000,
  96. effectDuration: 120000,
  97. effectDelay: 60000,
  98. realisticClouds: false,
  99. hideSun: false,
  100. hideSnow: false,
  101. hideSnowman: true,
  102. hideRain: false,
  103. hideFlower: true,
  104. hideClouds: false,
  105. hideFog: false,
  106. hideLightning: false,
  107. lightning1Count: 2,
  108. lightning2Count: 3,
  109. sequential: "",
  110. sunImage: "sun_right",
  111. effects: [] as Effect[],
  112. },
  113. start: function () {
  114. Log.info("Starting MMM-DynamicWeather");
  115. this.now = new Date();
  116. this.initialized = false;
  117. this.weatherLoaded = false;
  118. this.holidayLoaded = false;
  119. this.doShowEffects = true;
  120. this.hasDateEffectsToDisplay = false;
  121. this.hasHolidayEffectsToDisplay = false;
  122. this.hasWeatherEffectsToDisplay = true;
  123. this.effectDurationTimeout = null;
  124. this.effectDelayTimeout = null;
  125. this.weatherTimeout = null;
  126. this.holidayTimeout = null;
  127. this.allEffects = [] as Effect[];
  128. this.url = "https://api.openweathermap.org/data/2.5/weather?appid=" + this.config.api_key;
  129. if (this.config.lat && this.config.lon) {
  130. this.url += "&lat=" + this.config.lat + "&lon=" + this.config.lon;
  131. }
  132. if (this.config.locationID) {
  133. this.url += "&id=" + this.config.locationID;
  134. }
  135. this.snowEffect = new Effect();
  136. this.snowEffect.images = ["snow1.png", "snow2.png", "snow3.png"];
  137. this.snowEffect.size = 1;
  138. this.snowEffect.direction = "down";
  139. (this.realisticCloudsEffect as Effect) = new Effect();
  140. (this.realisticCloudsEffect as Effect).size = 15;
  141. (this.realisticCloudsEffect as Effect).direction = "left-right";
  142. (this.realisticCloudsEffect as Effect).images = ["cloud1.png", "cloud2.png"];
  143. this.weatherCode = 0;
  144. this.allHolidays = [] as string[];
  145. let count = 0;
  146. this.config.effects.forEach((configEffect) => {
  147. var effect = new Effect();
  148. effect.clone(configEffect);
  149. effect.id = count;
  150. count++;
  151. this.allEffects.push(effect);
  152. this.allHolidays.push(effect.holiday);
  153. });
  154. this.lastSequentialId = -1;
  155. if (this.config.sequential) {
  156. if (this.config.sequential == "effect" || this.config.sequential == "effect-one") {
  157. this.lastSequential = "weather";
  158. } else if (this.config.sequential == "weather") {
  159. this.lastSequential = "effect";
  160. } else {
  161. this.lastSequential = "";
  162. }
  163. } else {
  164. this.lastSequential = "";
  165. }
  166. this.checkDates();
  167. if (this.allHolidays.length > 0) {
  168. this.getHolidays(this);
  169. } else {
  170. this.holidayLoaded = true;
  171. }
  172. if (!this.config.alwaysDisplay) {
  173. this.getWeather(this);
  174. } else {
  175. this.weatherLoaded = true;
  176. }
  177. Log.info("[MMM-DynamicWeather] Finished initialization");
  178. },
  179. getStyles: function () {
  180. return ["MMM-DynamicWeather.css"];
  181. },
  182. checkDates: function () {
  183. try {
  184. (this.allEffects as Effect[]).forEach((effect) => {
  185. var effectMonth = effect.month - 1;
  186. if (!effect.hasWeatherCode() && !effect.hasHoliday()) {
  187. //if there is weatherCode or holiday, dates are ignored
  188. if (effect.getMonth() == 0 && effect.getDay() == 0 && effect.getYear() == 0) {
  189. //if no weatherCode, no holiday and no dates, then always display it
  190. if (effect.recurrence == "weekdays") {
  191. //if its not Sunday (0) and not Saturday (6)
  192. if (this.now.getDay() !== 6 && this.now.getDay() !== 0) {
  193. this.hasDateEffectsToDisplay = true;
  194. effect.doDisplay = true;
  195. }
  196. } else if (effect.recurrence == "weekends") {
  197. //if its Sunday (0) or Saturday (6)
  198. if (this.now.getDay() == 6 || this.now.getDay() == 0) {
  199. this.hasDateEffectsToDisplay = true;
  200. effect.doDisplay = true;
  201. }
  202. } else {
  203. this.hasDateEffectsToDisplay = true;
  204. effect.doDisplay = true;
  205. }
  206. } else {
  207. //if the month and date match or the month, date and year match
  208. if (this.now.getMonth() == effectMonth && this.now.getDate() == effect.day) {
  209. if (effect.getYear() == 0 || this.now.getFullYear() == effect.getYear()) {
  210. this.hasDateEffectsToDisplay = true;
  211. effect.doDisplay = true;
  212. }
  213. } else if (effect.recurrence == "monthly") {
  214. //ignore everything but the day
  215. if (this.now.getDate() == effect.getDay()) {
  216. this.hasDateEffectsToDisplay = true;
  217. effect.doDisplay = true;
  218. }
  219. } else if (effect.recurrence == "weekly") {
  220. var effectDay = new Date(effect.getYear(), effectMonth, effect.getDay());
  221. if (this.now.getDay() == effectDay.getDay()) {
  222. this.hasDateEffectsToDisplay = true;
  223. effect.doDisplay = true;
  224. }
  225. }
  226. }
  227. }
  228. });
  229. } catch (error) {
  230. console.error("[MMM-DynamicWeather] Error occured in checkDates: ", error);
  231. }
  232. },
  233. getDom: function () {
  234. var wrapper = document.createElement("div");
  235. wrapper.style.zIndex = this.config.zIndex;
  236. wrapper.style.opacity = this.config.opacity;
  237. wrapper.className = "wrapper";
  238. try {
  239. //setup the fade-out animation
  240. var fadeDuration = parseInt(this.config.fadeDuration);
  241. var animationDelay = parseInt(this.config.effectDuration) - fadeDuration;
  242. var fadeCSS = document.createElement("style");
  243. fadeCSS.innerHTML = ".fade-out {animation-name: fade; animation-duration: " + fadeDuration + "ms; animation-delay: " + animationDelay + "ms;}";
  244. wrapper.prepend(fadeCSS);
  245. wrapper.onanimationend = (e) => {
  246. //delay finished, elements faded out, now remove
  247. var thisAnimation = e.animationName;
  248. if (thisAnimation == "fade") {
  249. wrapper.remove();
  250. }
  251. };
  252. if (this.config.alwaysDisplay) {
  253. switch (this.config.alwaysDisplay) {
  254. case "snow": {
  255. this.showCustomEffect(wrapper, this.snowEffect);
  256. if (this.config.hideSnowman === false || this.config.hideSnowman === "false") {
  257. this.buildSnowman(wrapper);
  258. }
  259. break;
  260. }
  261. case "sun": {
  262. this.makeItSunny(wrapper);
  263. break;
  264. }
  265. case "rain": {
  266. this.makeItRain(wrapper);
  267. if (this.config.hideFlower === false || this.config.hideFlower === "false") {
  268. this.buildFlower(wrapper);
  269. }
  270. break;
  271. }
  272. case "lightning": {
  273. this.makeItLightning(wrapper);
  274. break;
  275. }
  276. case "rain-lightning": {
  277. this.makeItRain(wrapper);
  278. this.makeItLightning(wrapper);
  279. break;
  280. }
  281. case "cloudy": {
  282. if (this.config.realisticClouds) {
  283. this.showCustomEffect(wrapper, this.realisticCloudsEffect);
  284. } else {
  285. this.makeItCloudy(wrapper);
  286. }
  287. break;
  288. }
  289. case "fog": {
  290. this.makeItFoggy(wrapper);
  291. break;
  292. }
  293. default: {
  294. console.error("[MMM-DynamicWeather] Invalid config option 'alwaysDisplay'");
  295. }
  296. }
  297. return wrapper;
  298. }
  299. if (!this.weatherLoaded || !this.holidayLoaded) return wrapper; //need to wait for the weather to first be loaded
  300. if (!this.doShowEffects) return wrapper;
  301. wrapper.className = "wrapper fade-out";
  302. let showEffects = false;
  303. let showWeather = false;
  304. //check to see what should be shown based on availability and sequential
  305. if (this.hasDateEffectsToDisplay || this.hasHolidayEffectsToDisplay || this.hasWeatherEffectsToDisplay) {
  306. if (this.lastSequential == "effect" && this.hasWeatherEffectsToDisplay) {
  307. //if its weather's turn and there are weather to show
  308. showWeather = true;
  309. this.lastSequential = "weather";
  310. } else if (this.lastSequential == "weather" && (this.hasDateEffectsToDisplay || this.hasHolidayEffectsToDisplay)) {
  311. //if its effect's turn and there are effects to show
  312. showEffects = true;
  313. this.lastSequential = "effect";
  314. } else {
  315. showWeather = true;
  316. showEffects = true;
  317. }
  318. }
  319. if (showEffects) {
  320. for (let effect of this.allEffects as Effect[]) {
  321. if (effect.doDisplay) {
  322. //we can display this effect
  323. if (this.config.sequential == "effect-one") {
  324. //only show one effect at a time. if it wasn't the last one and its next in line, then do show it
  325. if (this.lastSequentialId < effect.id) {
  326. this.lastSequentialId = effect.id;
  327. if (this.allEffects.length - 1 == this.lastSequentialId) {
  328. //reached end of effects, reset
  329. this.lastSequentialId = -1;
  330. }
  331. this.showCustomEffect(wrapper, effect);
  332. break;
  333. }
  334. } else {
  335. this.showCustomEffect(wrapper, effect);
  336. }
  337. }
  338. }
  339. }
  340. if (showWeather) {
  341. //Codes from https://openweathermap.org/weather-conditions
  342. if (this.weatherCode >= 600 && this.weatherCode <= 622 && !this.config.hideSnow) {
  343. this.showCustomEffect(wrapper, this.snowEffect);
  344. if (this.config.hideSnowman === false || this.config.hideSnowman === "false") {
  345. this.buildSnowman(wrapper);
  346. }
  347. if (this.weatherCode >= 611 && this.weatherCode <= 622 && !this.config.hideRain) {
  348. //snow/rain mix
  349. this.makeItRain(wrapper);
  350. }
  351. } else if (this.weatherCode >= 200 && this.weatherCode <= 531 && !this.config.hideRain) {
  352. this.makeItRain(wrapper);
  353. if (this.config.hideFlower === false || this.config.hideFlower === "false") {
  354. this.buildFlower(wrapper);
  355. }
  356. if (this.weatherCode >= 200 && this.weatherCode <= 232 && !this.config.hideLightning) {
  357. this.makeItLightning(wrapper);
  358. }
  359. } else if (this.weatherCode >= 801 && this.weatherCode <= 804 && !this.config.hideClouds) {
  360. if (this.config.realisticClouds) {
  361. if (this.weatherCode == 801) {
  362. (this.realisticCloudsEffect as Effect).size = 8;
  363. (this.realisticCloudsEffect as Effect).particleCount = 30;
  364. (this.realisticCloudsEffect as Effect).images = ["cloud1.png"];
  365. } else if (this.weatherCode == 802) {
  366. (this.realisticCloudsEffect as Effect).size = 8;
  367. (this.realisticCloudsEffect as Effect).particleCount = 50;
  368. (this.realisticCloudsEffect as Effect).images = ["cloud1.png", "cloud2.png"];
  369. } else if (this.weatherCode == 803) {
  370. (this.realisticCloudsEffect as Effect).size = 15;
  371. (this.realisticCloudsEffect as Effect).particleCount = 30;
  372. (this.realisticCloudsEffect as Effect).images = ["cloud1.png", "cloud2.png"];
  373. } else if (this.weatherCode == 804) {
  374. (this.realisticCloudsEffect as Effect).size = 15;
  375. (this.realisticCloudsEffect as Effect).particleCount = 30;
  376. (this.realisticCloudsEffect as Effect).images = ["cloud3.png", "cloud2.png", "cloud1.png"];
  377. }
  378. this.showCustomEffect(wrapper, this.realisticCloudsEffect);
  379. } else {
  380. this.makeItCloudy(wrapper);
  381. }
  382. } else if (this.weatherCode >= 701 && this.weatherCode <= 781 && !this.config.hideFog) {
  383. this.makeItFoggy(wrapper);
  384. }else if (this.weatherCode == 800 && !this.config.hideSun) {
  385. this.makeItSunny(wrapper);
  386. }
  387. }
  388. console.info("[MMM-DynamicWeather] Displaying effects for: ", this.config.effectDuration);
  389. this.effectDurationTimeout = setTimeout(this.stopEffect, this.config.effectDuration, this);
  390. } catch (error) {
  391. console.error("[MMM-DynamicWeather] Error occured in getDom: ", error);
  392. }
  393. return wrapper;
  394. },
  395. showCustomEffect: function (wrapper: HTMLDivElement, effect: Effect) {
  396. this.doShowEffects = false;
  397. var flake, jiggle, size;
  398. var particleCount = effect.getParticleCount();
  399. if (particleCount < 0) {
  400. particleCount = this.config.particleCount;
  401. }
  402. for (var i = 0; i < particleCount; i++) {
  403. size = effect.getSize(); // * (Math.random() * 0.75) + 0.25;
  404. let flakeImage = document.createElement("div");
  405. let maxNum = effect.images.length;
  406. var picIndex = Math.floor(Math.random() * (maxNum - 0) + 0);
  407. flakeImage.style.backgroundImage = "url('./modules/MMM-DynamicWeather/images/" + effect.images[picIndex] + "')";
  408. flakeImage.style.transform = "scale(" + size + ", " + size + ")";
  409. flakeImage.style.opacity = size;
  410. flake = document.createElement("div");
  411. var animationName;
  412. switch (effect.direction) {
  413. case "down": {
  414. flake.className = "flake-downwards";
  415. flake.style.left = Math.random() * 100 - 10 + "%";
  416. animationName = "flake-jiggle";
  417. break;
  418. }
  419. case "left-right": {
  420. flake.className = "flake-left-right";
  421. flake.style.left = -75 - size * 2 + "px";
  422. flake.style.top = Math.random() * 100 - 10 + "%";
  423. flake.style.animationName = "flake-jiggle-left-right";
  424. break;
  425. }
  426. case "right-left": {
  427. flake.className = "flake-right-left";
  428. flake.style.right = 75 + size * 2 + "px";
  429. flake.style.top = Math.random() * 100 - 10 + "%";
  430. flake.style.animationName = "flake-jiggle-right-left";
  431. break;
  432. }
  433. default: {
  434. flake.className = "flake-upwards";
  435. flake.style.left = Math.random() * 100 - 10 + "%";
  436. animationName = "flake-jiggle";
  437. break;
  438. }
  439. }
  440. let max = effect.getSpeedMax();
  441. let min = effect.getSpeedMin();
  442. jiggle = document.createElement("div");
  443. jiggle.style.animationDelay = Math.random() * max + "s";
  444. jiggle.style.animationDuration = max - Math.random() * min * size + "s";
  445. if (animationName) {
  446. jiggle.style.animationName = animationName;
  447. }
  448. jiggle.appendChild(flakeImage);
  449. size = Math.random() * 0.75 + 0.25;
  450. jiggle.style.transform = "scale(" + size + ", " + size + ")";
  451. jiggle.style.opacity = size;
  452. if (animationName) {
  453. jiggle.style.animationName = animationName;
  454. }
  455. flake.appendChild(jiggle);
  456. flake.style.animationDelay = Math.random() * max + "s";
  457. flake.style.animationDuration = max - Math.random() * min * size + "s";
  458. wrapper.appendChild(flake);
  459. }
  460. },
  461. buildSnowman: function (wrapper) {
  462. this.doShowEffects = false;
  463. var snowmanImage = document.createElement("div");
  464. snowmanImage.classList.add("snowman");
  465. snowmanImage.style.animationDuration = this.config.effectDuration - 10000 + "ms"; //subtract for 10s delay
  466. wrapper.appendChild(snowmanImage);
  467. },
  468. makeItRain: function (wrapper) {
  469. this.doShowEffects = false;
  470. var increment = 0;
  471. while (increment < this.config.particleCount) {
  472. var randoHundo = Math.floor(Math.random() * (98 - 1 + 1) + 1); //random number between 98 and 1
  473. var randoFiver = Math.floor(Math.random() * (5 - 2 + 1) + 2);
  474. increment += randoFiver;
  475. let frontDrop = document.createElement("div");
  476. frontDrop.classList.add("drop");
  477. frontDrop.style.left = increment + "%";
  478. frontDrop.style.bottom = randoFiver + randoFiver - 1 + 100 + "%";
  479. frontDrop.style.animationDelay = "1." + randoHundo + "s";
  480. frontDrop.style.animationDuration = "1.5" + randoHundo + "s";
  481. let frontStem = document.createElement("div");
  482. frontStem.classList.add("stem");
  483. frontStem.style.animationDelay = "1." + randoHundo + "s";
  484. frontStem.style.animationDuration = "1.5" + randoHundo + "s";
  485. frontDrop.appendChild(frontStem);
  486. let backDrop = document.createElement("div");
  487. backDrop.classList.add("drop");
  488. backDrop.style.opacity = "0.5";
  489. backDrop.style.right = increment + "%";
  490. backDrop.style.bottom = randoFiver + randoFiver - 1 + 100 + "%";
  491. backDrop.style.animationDelay = "1." + randoHundo + "s";
  492. backDrop.style.animationDuration = "1.5" + randoHundo + "s";
  493. let backStem = document.createElement("div");
  494. backStem.classList.add("stem");
  495. backStem.style.animationDelay = "1." + randoHundo + "s";
  496. backStem.style.animationDuration = "1.5" + randoHundo + "s";
  497. backDrop.appendChild(backStem);
  498. wrapper.appendChild(backDrop);
  499. wrapper.appendChild(frontDrop);
  500. }
  501. },
  502. buildFlower: function (wrapper) {
  503. this.doShowEffects = false;
  504. var flowerImage = document.createElement("div");
  505. flowerImage.classList.add("flower");
  506. flowerImage.style.animationDuration = this.config.effectDuration - 10000 + "ms"; //subtract for 10s delay
  507. wrapper.appendChild(flowerImage);
  508. },
  509. makeItLightning: function (wrapper) {
  510. this.doShowEffects = false;
  511. var lightningImage1 = document.createElement("div");
  512. lightningImage1.classList.add("lightning1");
  513. lightningImage1.style.animationIterationCount = this.config.lightning1Count;
  514. var lightningImage2 = document.createElement("div");
  515. lightningImage2.classList.add("lightning2");
  516. lightningImage2.style.animationIterationCount = this.config.lightning2Count;
  517. let lightningPlayer = document.createElement("div");
  518. lightningPlayer.classList.add("lightningPlayer");
  519. lightningPlayer.appendChild(lightningImage1);
  520. lightningPlayer.appendChild(lightningImage2);
  521. wrapper.appendChild(lightningPlayer);
  522. },
  523. makeItSunny: function (wrapper) {
  524. this.doShowEffects = false;
  525. var sunImage = document.createElement("div");
  526. sunImage.classList.add("sun");
  527. sunImage.style.background = "url('./modules/MMM-DynamicWeather/images/" + this.config.sunImage + ".png') center center/cover no-repeat transparent";
  528. let sunPlayer = document.createElement("div");
  529. sunPlayer.classList.add("sunPlayer");
  530. sunPlayer.appendChild(sunImage);
  531. wrapper.appendChild(sunPlayer);
  532. },
  533. makeItCloudy: function (wrapper) {
  534. this.doShowEffects = false;
  535. var increment = 0;
  536. while (increment < this.config.particleCount) {
  537. var randNum = Math.floor(Math.random() * (25 - 5 + 1) + 5); //random number between 25 and 5
  538. var speed = Math.floor(Math.random() * (35 - 15 + 1) + 15);
  539. var size = Math.floor(Math.random() * (60 - 3 + 1) + 3);
  540. increment += randNum;
  541. let cloudBase = document.createElement("div");
  542. cloudBase.style.animation = "animateCloud " + speed + "s linear infinite";
  543. cloudBase.style.transform = "scale(0." + size + ")";
  544. let cloud = document.createElement("div");
  545. cloud.classList.add("cloud");
  546. cloudBase.appendChild(cloud);
  547. wrapper.appendChild(cloudBase);
  548. }
  549. },
  550. makeItFoggy: function (wrapper) {
  551. this.doShowEffects = false;
  552. var fogImage1 = document.createElement("div");
  553. fogImage1.classList.add("image01");
  554. var fogImage2 = document.createElement("div");
  555. fogImage2.classList.add("image02");
  556. let fogPlayer1 = document.createElement("div");
  557. fogPlayer1.id = "foglayer_01";
  558. fogPlayer1.classList.add("fog");
  559. fogPlayer1.appendChild(fogImage1);
  560. fogPlayer1.appendChild(fogImage2);
  561. wrapper.appendChild(fogPlayer1);
  562. fogImage1 = document.createElement("div");
  563. fogImage1.classList.add("image01");
  564. fogImage2 = document.createElement("div");
  565. fogImage2.classList.add("image02");
  566. let fogPlayer2 = document.createElement("div");
  567. fogPlayer2.id = "foglayer_02";
  568. fogPlayer2.classList.add("fog");
  569. fogPlayer2.appendChild(fogImage1);
  570. fogPlayer2.appendChild(fogImage2);
  571. wrapper.appendChild(fogPlayer2);
  572. fogImage1 = document.createElement("div");
  573. fogImage1.classList.add("image01");
  574. fogImage2 = document.createElement("div");
  575. fogImage2.classList.add("image02");
  576. let fogPlayer3 = document.createElement("div");
  577. fogPlayer3.id = "foglayer_03";
  578. fogPlayer3.classList.add("fog");
  579. fogPlayer3.appendChild(fogImage1);
  580. fogPlayer3.appendChild(fogImage2);
  581. wrapper.appendChild(fogPlayer3);
  582. },
  583. stopEffect: function (_this) {
  584. try {
  585. //wait for delay and reset
  586. _this.updateDom();
  587. let delay = _this.config.effectDelay;
  588. _this.effectDelayTimeout = setTimeout(
  589. (_that, _effect) => {
  590. _that.doShowEffects = true;
  591. _that.updateDom();
  592. },
  593. delay,
  594. _this
  595. );
  596. } catch (error) {
  597. console.error("[MMM-DynamicWeather] Error occured in stopping effects: ", error);
  598. }
  599. },
  600. getWeather: function (_this) {
  601. _this.sendSocketNotification("API-Fetch", _this.url);
  602. _this.weatherTimeout = setTimeout(_this.getWeather, _this.config.weatherInterval, _this);
  603. },
  604. getHolidays: function (_this) {
  605. try {
  606. _this.sendSocketNotification("Holiday-Fetch", {});
  607. var today = new Date();
  608. var tomorrow = new Date();
  609. tomorrow.setDate(today.getDate() + 1);
  610. tomorrow.setHours(0, 0, 0, 0);
  611. var msTillMidnight = tomorrow.getTime() - today.getTime();
  612. console.info("[MMM-DynamicWeather] Holidays have been fetched, waiting till midnight (" + msTillMidnight + " ms) to reset.");
  613. _this.holidayTimeout = setTimeout(_this.resetHolidays, msTillMidnight, _this);
  614. } catch (error) {
  615. console.error("[MMM-DynamicWeather] Error occured in getHolidays: ", error);
  616. }
  617. },
  618. resetHolidays: function (_this) {
  619. try {
  620. console.info("[MMM-DynamicWeather] Resetting holidays...");
  621. //Reset all effects with a holiday to not show, we will trigger another getHolidays to see if the next day has another holiday to display next
  622. (_this.allEffects as Effect[]).forEach((effect) => {
  623. if (effect.holiday) {
  624. effect.doDisplay = false;
  625. }
  626. });
  627. _this.hasHolidayEffectsToDisplay = false;
  628. _this.updateDom();
  629. console.info("[MMM-DynamicWeather] Holidays reset.");
  630. _this.getHolidays(_this);
  631. } catch (error) {
  632. console.error("[MMM-DynamicWeather] Error occured in resetting holidays: ", error);
  633. }
  634. },
  635. parseHolidays: function (body: string) {
  636. let today = new Date();
  637. let todayHolidays = [];
  638. todayHolidays.push("test");
  639. try {
  640. var parser = new DOMParser();
  641. var doc = parser.parseFromString(body, "text/html");
  642. var children = doc.getElementById("holidays-table").children[1].children;
  643. for (var i = 0; i < children.length; i++) {
  644. var child1 = children[i];
  645. if (child1.hasAttribute("data-date")) {
  646. var holidayDateStr = child1.getAttribute("data-date");
  647. var child2 = child1.children;
  648. for (var j = 0; j < child2.length; j++) {
  649. var child3 = child2[j];
  650. if (child3.hasChildNodes()) {
  651. for (var k = 0; k < child3.children.length; k++) {
  652. var child4 = child3.children[k];
  653. for (var l = 0; l < this.allHolidays.length; l++) {
  654. var effectHoliday = this.allHolidays[l];
  655. if (child4.textContent == effectHoliday) {
  656. var holidayDate = new Date(parseInt(holidayDateStr));
  657. if (holidayDate.getUTCDate() == today.getDate() && holidayDate.getUTCMonth() == today.getMonth()) {
  658. todayHolidays.push(effectHoliday);
  659. }
  660. }
  661. }
  662. }
  663. }
  664. }
  665. }
  666. }
  667. } catch (error) {
  668. console.error("[MMM-DynamicWeather] Error occured in parsing holidays: ", error);
  669. }
  670. return todayHolidays;
  671. },
  672. socketNotificationReceived: function (notification, payload) {
  673. try {
  674. if (notification === "API-Received" && payload.url === this.url) {
  675. this.weatherLoaded = true;
  676. if (!payload.success) {
  677. console.error("[MMM-DynamicWeather] API-Receieved failure status");
  678. return;
  679. }
  680. let newCode = payload.result.weather[0].id;
  681. let doUpdate = false;
  682. //check to see if the newCode is different than already displayed, and if so, is it going to show anything
  683. if (newCode != this.weatherCode) {
  684. this.weatherCode = newCode;
  685. if (newCode >= 600 && newCode <= 622 && !this.config.hideSnow) {
  686. doUpdate = true;
  687. }
  688. if ((newCode >= 200 && newCode <= 531) || (newCode >= 611 && newCode <= 622 && !this.config.hideRain)) {
  689. doUpdate = true;
  690. }
  691. if (newCode >= 200 && newCode <= 232 && !this.config.hideLightning) {
  692. doUpdate = true;
  693. }
  694. if (newCode >= 801 && newCode <= 804 && !this.config.hideClouds) {
  695. doUpdate = true;
  696. }
  697. if (newCode >= 701 && newCode <= 781 && !this.config.hideFog) {
  698. doUpdate = true;
  699. }
  700. if (newCode == 800 && !this.config.hideSun) {
  701. doUpdate = true;
  702. }
  703. (this.allEffects as Effect[]).forEach((effect) => {
  704. if (effect.getWeatherCode() == newCode || (effect.getMinWeatherCode() <= newCode && effect.getMaxWeatherCode() >= newCode)) {
  705. doUpdate = true;
  706. effect.doDisplay = true;
  707. this.hasWeatherEffectsToDisplay = true;
  708. }
  709. });
  710. }
  711. //only update the dom if the weather is different (unless holiday or date effects exist and holiday has finished loading)
  712. if (doUpdate || (this.holidayLoaded && (this.hasDateEffectsToDisplay || this.hasHolidayEffectsToDisplay))) {
  713. this.doShowEffects = true;
  714. clearTimeout(this.effectDurationTimeout);
  715. clearTimeout(this.effectDelayTimeout);
  716. this.updateDom();
  717. }
  718. }
  719. if (notification === "Holiday-Received") {
  720. this.holidayLoaded = true;
  721. if (!payload.success) {
  722. console.error("[MMM-DynamicWeather] Holiday-Receieved failure status");
  723. return;
  724. }
  725. let doUpdate = false;
  726. let todayHolidays = [] as string[];
  727. todayHolidays = this.parseHolidays(payload.result.holidayBody);
  728. //returned a list of holidays for today, check to see if any effects have the same holiday name, if so display them and update dom
  729. (this.allEffects as Effect[]).forEach((effect) => {
  730. todayHolidays.forEach((holidayName) => {
  731. if (effect.holiday == holidayName) {
  732. doUpdate = true;
  733. effect.doDisplay = true;
  734. this.hasHolidayEffectsToDisplay = true;
  735. }
  736. });
  737. });
  738. //only update the dom if the effects have a holiday to show today (unless weather and date effects exist and weather has finished loading)
  739. if (doUpdate || (this.weatherLoaded && (this.hasDateEffectsToDisplay || this.hasWeatherEffectsToDisplay))) {
  740. this.doShowEffects = true;
  741. clearTimeout(this.effectDurationTimeout);
  742. clearTimeout(this.effectDelayTimeout);
  743. this.updateDom();
  744. }
  745. }
  746. } catch (error) {
  747. console.error("[MMM-DynamicWeather] Error occured in notification received: ", error);
  748. }
  749. },
  750. });