@sdetweil @chrisfr1976 you all are amazing. all i had to do was change the fireworks to fullscreen_below and now it all works. the text box is clickable and editable and the chores list sets off the fireworks and i see them. thank you both. i enjoy learning and doing all this.
Read the statement by Michael Teeuw here.
Posts
-
RE: I need Help. my shopping list is not clickable
-
RE: I need Help. my shopping list is not clickable
@chrisfr1976 thanks. i will try this. i have no issues with the fireworks module firing when my chore list is completed. but it may be interfering with the clickable chore list.
-
RE: I need Help. my shopping list is not clickable
@sdetweil honestly at this point I have no idea what i’m doing. I have another module chore list that has clickable toggles and button and even activates fireworks once everything is marked done and it works perfectly fine
-
I need Help. my shopping list is not clickable
so I’m not sure if this is the right area but, I built a module to create a real time editable shopping list. the core is there but I cannot click into the box to add an item. when I check the dev logs it shows no issues and when I exit dev logs it lets me type into the box, but I cannot add the item. once I click out the box I cannot click in it. I’m stumped and even resorted to AI for help with zero luck. what am I missing? any help would be greatly appreciated.
Module.register("MMM-ShoppingList", { defaults: { initialItems: [] // Initial items in the shopping list }, start: function() { console.log("Starting module: " + this.name); this.shoppingList = this.config.initialItems; }, getStyles: function() { return ["MMM-ShoppingList.css"]; }, getDom: function() { var wrapper = document.createElement("div"); wrapper.id = "shopping-list-container"; var heading = document.createElement("h1"); heading.innerText = "Shopping List"; wrapper.appendChild(heading); var list = document.createElement("ul"); list.id = "shopping-list"; wrapper.appendChild(list); for (let item of this.shoppingList) { list.appendChild(this.createListItem(item)); } var inputDiv = document.createElement("div"); inputDiv.id = "add-item"; var input = document.createElement("input"); input.type = "text"; input.id = "new-item"; input.placeholder = "Add new item..."; inputDiv.appendChild(input); var button = document.createElement("button"); button.innerText = "Add"; button.onclick = () => { alert("Button clicked"); this.addItem(input.value); input.value = ""; }; inputDiv.appendChild(button); wrapper.appendChild(inputDiv); console.log('Returning wrapper with interactive elements'); console.log('DOM:', wrapper); return wrapper; }, createListItem: function(item) { var li = document.createElement("li"); li.innerText = item; var deleteSpan = document.createElement("span"); deleteSpan.className = "delete"; deleteSpan.innerHTML = "❌"; deleteSpan.onclick = () => { alert("Delete clicked"); this.removeItem(item); }; li.appendChild(deleteSpan); return li; }, addItem: function(item) { if (item === "") return; console.log('Adding item:', item); this.shoppingList.push(item); this.renderShoppingList(); console.log('Item added:', item); }, removeItem: function(item) { console.log('Removing item:', item); this.shoppingList = this.shoppingList.filter(i => i !== item); this.renderShoppingList(); console.log('Item removed:', item); }, renderShoppingList: function() { var list = document.getElementById("shopping-list"); if (list) { list.innerHTML = ""; for (let item of this.shoppingList) { console.log('Rendering item:', item); list.appendChild(this.createListItem(item)); } console.log('DOM updated with new list'); } else { console.log('Error: Shopping list container not found'); } }, notificationReceived: function(notification, payload, sender) { console.log('Notification received:', notification); if (notification === "DOM_OBJECTS_CREATED") { console.log('DOM_OBJECTS_CREATED notification received'); this.renderShoppingList(); } } });
the Dev Log:
Initializing MagicMirror². translator.js:116 Loading core translation file: translations/en.json VM4 sandbox_bundle:2 Electron Security Warning (Insecure Content-Security-Policy) This renderer process has either no Content Security Policy set or a policy with "unsafe-eval" enabled. This exposes users of this app to unnecessary security risks. For more information and help, consult https://electronjs.org/docs/tutorial/security. This warning will not show up once the app is packaged. warnAboutInsecureCSP @ VM4 sandbox_bundle:2 translator.js:132 Loading core translation fallback file: translations/en.json loader.js:178 Load script: modules/MMM-Cursor/MMM-Cursor.js module.js:489 Module registered: MMM-Cursor loader.js:151 Bootstrapping module: MMM-Cursor loader.js:155 Scripts loaded for: MMM-Cursor loader.js:194 Load stylesheet: modules/MMM-Cursor/MMM-Cursor.css loader.js:158 Styles loaded for: MMM-Cursor loader.js:161 Translations loaded for: MMM-Cursor loader.js:178 Load script: modules/default/alert/alert.js module.js:489 Module registered: alert loader.js:151 Bootstrapping module: alert loader.js:178 Load script: modules/default/alert/notificationFx.js loader.js:155 Scripts loaded for: alert loader.js:194 Load stylesheet: vendor/css/font-awesome.css loader.js:194 Load stylesheet: modules/default/alert/./styles/notificationFx.css loader.js:194 Load stylesheet: modules/default/alert/./styles/center.css loader.js:158 Styles loaded for: alert translator.js:99 alert - Load translation: translations/en.json translator.js:99 alert - Load translation fallback: translations/bg.json loader.js:161 Translations loaded for: alert loader.js:178 Load script: modules/default/clock/clock.js module.js:489 Module registered: clock loader.js:151 Bootstrapping module: clock loader.js:178 Load script: vendor/node_modules/moment/min/moment-with-locales.js loader.js:178 Load script: vendor/node_modules/moment-timezone/builds/moment-timezone-with-data.js loader.js:178 Load script: vendor/node_modules/suncalc/suncalc.js loader.js:155 Scripts loaded for: clock loader.js:194 Load stylesheet: modules/default/clock/clock_styles.css loader.js:158 Styles loaded for: clock loader.js:161 Translations loaded for: clock loader.js:178 Load script: modules/MMM-WiFiPassword/MMM-WiFiPassword.js module.js:489 Module registered: MMM-WiFiPassword loader.js:151 Bootstrapping module: MMM-WiFiPassword loader.js:178 Load script: modules/MMM-WiFiPassword/qrcode.min.js loader.js:194 Load stylesheet: modules/MMM-WiFiPassword/MMM-WiFiPassword.css loader.js:155 Scripts loaded for: MMM-WiFiPassword loader.js:158 Styles loaded for: MMM-WiFiPassword loader.js:161 Translations loaded for: MMM-WiFiPassword loader.js:178 Load script: modules/MMM-ShoppingList/MMM-ShoppingList.js module.js:489 Module registered: MMM-ShoppingList loader.js:151 Bootstrapping module: MMM-ShoppingList loader.js:155 Scripts loaded for: MMM-ShoppingList loader.js:194 Load stylesheet: modules/MMM-ShoppingList/MMM-ShoppingList.css loader.js:158 Styles loaded for: MMM-ShoppingList loader.js:161 Translations loaded for: MMM-ShoppingList loader.js:178 Load script: modules/default/weather/weather.js module.js:489 Module registered: weather loader.js:151 Bootstrapping module: weather loader.js:254 File already loaded: moment.js loader.js:178 Load script: modules/default/weather/weatherutils.js loader.js:178 Load script: modules/default/weather/weatherobject.js loader.js:178 Load script: modules/default/weather/providers/overrideWrapper.js loader.js:178 Load script: modules/default/weather/weatherprovider.js loader.js:254 File already loaded: suncalc.js loader.js:178 Load script: modules/default/weather/providers/openmeteo.js loader.js:155 Scripts loaded for: weather loader.js:254 File already loaded: font-awesome.css loader.js:194 Load stylesheet: vendor/node_modules/weathericons/css/weather-icons.css loader.js:194 Load stylesheet: modules/default/weather/weather.css loader.js:158 Styles loaded for: weather loader.js:161 Translations loaded for: weather loader.js:151 Bootstrapping module: weather loader.js:254 File already loaded: moment.js loader.js:254 File already loaded: weatherutils.js loader.js:254 File already loaded: weatherobject.js loader.js:254 File already loaded: modules/default/weather/providers/overrideWrapper.js loader.js:254 File already loaded: weatherprovider.js loader.js:254 File already loaded: suncalc.js loader.js:254 File already loaded: modules/default/weather/providers/openmeteo.js loader.js:155 Scripts loaded for: weather loader.js:254 File already loaded: font-awesome.css loader.js:254 File already loaded: weather-icons.css loader.js:254 File already loaded: weather.css loader.js:158 Styles loaded for: weather loader.js:161 Translations loaded for: weather loader.js:178 Load script: modules/MMM-DailyChoreGridColor/MMM-DailyChoreGridColor.js module.js:489 Module registered: MMM-DailyChoreGridColor loader.js:151 Bootstrapping module: MMM-DailyChoreGridColor loader.js:155 Scripts loaded for: MMM-DailyChoreGridColor loader.js:158 Styles loaded for: MMM-DailyChoreGridColor loader.js:161 Translations loaded for: MMM-DailyChoreGridColor loader.js:178 Load script: modules/MMM-Fireworks/MMM-Fireworks.js module.js:489 Module registered: MMM-Fireworks loader.js:151 Bootstrapping module: MMM-Fireworks loader.js:178 Load script: https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.min.js loader.js:155 Scripts loaded for: MMM-Fireworks loader.js:158 Styles loaded for: MMM-Fireworks loader.js:161 Translations loaded for: MMM-Fireworks loader.js:194 Load stylesheet: css/custom.css MMM-Cursor.js:24 Starting module: MMM-Cursor alert.js:42 Starting module: alert clock.js:43 Starting module: clock MMM-ShoppingList.js:7 Starting module: MMM-ShoppingList weatherprovider.js:28 Weather provider: Open-Meteo initialized. weatherprovider.js:39 Weather provider: Open-Meteo started. weatherprovider.js:28 Weather provider: Open-Meteo initialized. weatherprovider.js:39 Weather provider: Open-Meteo started. main.js:601 All modules started! MMM-ShoppingList.js:103 Notification received: ALL_MODULES_STARTED MMM-Fireworks.js:68 Notification received: ALL_MODULES_STARTED MMM-ShoppingList.js:51 Returning wrapper with interactive elements MMM-ShoppingList.js:52 DOM: <div id="shopping-list-container"><h1>Shopping List</h1><ul id="shopping-list"><li>flex"Milk"<span class="delete">❌</span></li><li>flex"Eggs"<span class="delete">❌</span></li><li>flex"Bread"<span class="delete">❌</span></li></ul><div id="add-item"><input type="text" id="new-item" placeholder="Add new item..."><button>Add</button></div></div> MMM-ShoppingList.js:103 Notification received: MODULE_DOM_CREATED MMM-Fireworks.js:68 Notification received: MODULE_DOM_CREATED MMM-ShoppingList.js:103 Notification received: DOM_OBJECTS_CREATED MMM-ShoppingList.js:105 DOM_OBJECTS_CREATED notification received MMM-ShoppingList.js:93 Rendering item: Milk MMM-ShoppingList.js:93 Rendering item: Eggs MMM-ShoppingList.js:93 Rendering item: Bread MMM-ShoppingList.js:96 DOM updated with new list MMM-Fireworks.js:68 Notification received: DOM_OBJECTS_CREATED weather.js:164 New weather information available. MMM-ShoppingList.js:103 Notification received: CURRENTWEATHER_TYPE MMM-Fireworks.js:68 Notification received: CURRENTWEATHER_TYPE MMM-ShoppingList.js:103 Notification received: WEATHER_UPDATED MMM-Fireworks.js:68 Notification received: WEATHER_UPDATED weather.js:164 New weather information available. MMM-ShoppingList.js:103 Notification received: WEATHER_UPDATED MMM-Fireworks.js:68 Notification received: WEATHER_UPDATED
-
RE: triggering a module
@chrisfr1976 yes! it definitely helps! my kid is going to love it. i linked it to a chore list so when he successfully completes all his chores through out the week the fireworks will set off and give him a little party.
-
RE: Dynamic change in the size and position of modules
@TheBodger I am actually using your module, and it was working great until the the most recent update and now there is only one module i can move around the others wont let me. any suggestions would be helpful thank you
-
RE: triggering a module
@sdetweil gotcha thank you, i dont know all the terminology. but upon completing all tasks i does send the notification but nothing happens after that
-
RE: triggering a module
@sdetweil i apologize but i have no idea what that means
-
triggering a module
hello all im still new to all this and trying to work my way through all this, I created a chores module that when all chores were marked done it would trigger the MMM-Fireworks module. but since the fireworks module updated it no longer triggers the fireworks and now im at a loss on how to get it to work. any help is greatly appreciated.
here is my chores module:
Module.register("MMM-DailyChoreGridColor", { defaults: { updateInterval: 60000, // 1 minute }, start: function() { this.choreList = { MONDAY: [ { chore: "Wash dishes", done: false }, { chore: "Take Out Trash", done: false }, { chore: "Wipe Down Counters", done: false }, { chore: "Pick Up Poop", done: false }, { chore: "Feed Pets", done: false }, { chore: "Play With Petey", done: false }, { chore: "Read", done: false }], TUESDAY: [ { chore: "Wash dishes", done: false }, { chore: "Take Out Trash", done: false }, { chore: "Wipe Down Counters", done: false }, { chore: "Pick Up Poop", done: false }, { chore: "Feed Pets", done: false }, { chore: "Play With Petey", done: false }, { chore: "Read", done: false }], WEDNESDAY: [ { chore: "Wash dishes", done: false }, { chore: "Take Out Trash", done: false }, { chore: "Wipe Down Counters", done: false }, { chore: "Pick Up Poop", done: false }, { chore: "Feed Pets", done: false }, { chore: "Play With Petey", done: false }, { chore: "Read", done: false }], THURSDAY: [ { chore: "Wash dishes", done: false }, { chore: "Take Out Trash", done: false }, { chore: "Wipe Down Counters", done: false }, { chore: "Pick Up Poop", done: false }, { chore: "Feed Pets", done: false }, { chore: "Play With Petey", done: false }, { chore: "Read", done: false }], FRIDAY: [ { chore: "Wash dishes", done: false }, { chore: "Take Out Trash", done: false }, { chore: "Wipe Down Counters", done: false }, { chore: "Pick Up Poop", done: false }, { chore: "Feed Pets", done: false }, { chore: "Play With Petey", done: false }, { chore: "Read", done: false }], SATURDAY: [ { chore: "Wash dishes", done: false }, { chore: "Take Out Trash", done: false }, { chore: "Wipe Down Counters", done: false }, { chore: "Pick Up Poop", done: false }, { chore: "Feed Pets", done: false }, { chore: "Play With Petey", done: false }], SUNDAY: [ { chore: "Wash dishes", done: false }, { chore: "Take Out Trash", done: false }, { chore: "Wipe Down Counters", done: false }, { chore: "Pick Up Poop", done: false }, { chore: "Feed Pets", done: false }, { chore: "Play With Petey", done: false }, { chore: "Clean Room", done: false }, { chore: "Clean Bathroom", done: false }, { chore: "Laundry", done: false }], }; }, getDom: function() { const wrapper = document.createElement("div"); wrapper.innerHTML = "<h2>Daily Chore List</h2>"; const table = document.createElement("table"); table.style.width = "100%"; table.style.margin = "auto"; table.style.borderCollapse = "collapse"; const headerRow = document.createElement("tr"); const daysOfWeek = ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"]; daysOfWeek.forEach(day => { const dayHeader = document.createElement("th"); dayHeader.innerHTML = day; dayHeader.style.border = "1px solid black"; dayHeader.style.padding = "10px"; dayHeader.style.backgroundColor = "#f2f2f2"; dayHeader.style.textAlign = "center"; headerRow.appendChild(dayHeader); }); table.appendChild(headerRow); const maxChores = Math.max(...Object.values(this.choreList).map(day => day.length)); for (let i = 0; i < maxChores; i++) { const choreRow = document.createElement("tr"); daysOfWeek.forEach(day => { const choreCell = document.createElement("td"); const taskDiv = document.createElement("div"); const task = this.choreList[day][i]; if (task) { const taskText = document.createElement("span"); taskText.innerHTML = task.chore; const dropdown = document.createElement("select"); dropdown.options.add(new Option("X", "false")); dropdown.options.add(new Option("\u2713", "true")); dropdown.value = task.done ? "true" : "false"; this.updateDropdownColor(dropdown, task.done); dropdown.style.float = "right"; // Align dropdown to the right dropdown.onchange = () => { task.done = dropdown.value === "true"; this.updateDropdownColor(dropdown, task.done); console.log(`Task updated: ${task.chore}, Done: ${task.done}`); if (this.allChoresDone()) { console.log("All chores done, sending notification"); this.sendNotification("ALL_CHORES_DONE"); } else { console.log("Not all chores are done yet."); } }; taskDiv.appendChild(taskText); taskDiv.appendChild(dropdown); } choreCell.appendChild(taskDiv); choreCell.style.border = "1px solid black"; choreCell.style.padding = "10px"; choreCell.style.textAlign = "center"; choreRow.appendChild(choreCell); }); table.appendChild(choreRow); } const clearButton = document.createElement("button"); clearButton.innerHTML = "Clear All"; clearButton.style.marginTop = "20px"; clearButton.onclick = () => { Object.keys(this.choreList).forEach(day => { this.choreList[day].forEach(task => { task.done = false; }); }); this.updateDom(); console.log("All chores have been cleared."); }; wrapper.appendChild(clearButton); wrapper.appendChild(table); return wrapper; }, updateDropdownColor: function(dropdown, done) { if (done) { dropdown.style.backgroundColor = "green"; dropdown.style.color = "white"; } else { dropdown.style.backgroundColor = "red"; dropdown.style.color = "white"; } }, allChoresDone: function() { for (const day in this.choreList) { for (const task of this.choreList[day]) { if (!task.done) { console.log(`Task not done: ${task.chore}`); return false; } } } console.log("All chores done: true"); this.sendNotification("ALL_CHORES_DONE"); return true; } });
and here is the Fireworks module:
Module.register("MMM-Fireworks", { defaults: { startDateTime: "", // ISO format start time duration: 36000, // Duration in milliseconds (6 hours) // p5 Fireworks settings: fireworkSpawnChance: 0.5, // Chance each frame to spawn a new firework explosionParticleCount: 40, // Number of particles per explosion // Display settings: fullscreen: true, // If false, use the defined width/height. width: "400px", height: "500px", // Velocity settings for rocket particles: magnitude_high: -19, magnitude_low: -8, // Trailing effect transparency (alpha value for background): transparency: 10, // New canvas opacity (0.0 = fully transparent, 1.0 = fully opaque) canvasOpacity: 0.5, // Module management settings: disableAllModules: true, // Set to false so other modules remain visible. keepModules: [], // Array of module names to keep active. // Text overlay: text: "Good Job Buddy" }, start: function () { this.fireworksActive = false; this.disabledModules = []; this._p5Instance = null; }, // Create the module's container. // If fullscreen is true, force full screen; otherwise use the defined width/height. getDom: function () { const wrapper = document.createElement("div"); wrapper.id = "fireworksContainer"; if (this.config.fullscreen) { wrapper.style.position = "fixed"; wrapper.style.top = "0"; wrapper.style.left = "0"; wrapper.style.width = "100vw"; wrapper.style.height = "100vh"; wrapper.style.zIndex = "9999"; wrapper.style.background = "transparent"; } else { wrapper.style.position = "relative"; wrapper.style.width = this.config.width; wrapper.style.height = this.config.height; } // Create and append the text overlay element (initially hidden). if (this.config.text) { const textDiv = document.createElement("div"); textDiv.id = "fireworksText"; textDiv.className = "fireworks-text"; textDiv.innerHTML = this.config.text; textDiv.style.display = "none"; // Hidden by default; shown during fireworks. wrapper.appendChild(textDiv); } return wrapper; }, notificationReceived: function (notification, payload, sender) { console.log("Notification received: " + notification); if (notification === "ALL_CHORES_DONE") { this.startFireworks(); } else if (notification === "DOM_OBJECTS_CREATED") { this.scheduleFireworks(); } }, scheduleFireworks: function () { const MAX_DELAY = 2147483647; // Maximum delay in ms (~24.8 days) const startTime = new Date(this.config.startDateTime).getTime(); const currentTime = Date.now(); const duration = this.config.duration; if (currentTime < startTime) { let delay = startTime - currentTime; if (delay > MAX_DELAY) { setTimeout(() => this.scheduleFireworks(), MAX_DELAY); } else { setTimeout(() => this.startFireworks(), delay); } } else if (currentTime < startTime + duration) { this.startFireworks(); } else { console.warn("Fireworks time window has already passed."); } }, startFireworks: function () { this.fireworksActive = true; const container = document.getElementById("fireworksContainer"); container.classList.add("fullscreen"); // Show the text overlay during the fireworks period. const textDiv = document.getElementById("fireworksText"); if (textDiv) { textDiv.style.display = "block"; } // Do not hide other modules if disableAllModules is false. if (this.config.disableAllModules) { this.deactivateAndHideModules(); } this.initializeP5(); // Play fireworks sound const audio = new Audio("MagicMirror/Modules/MMM-Fireworks/fireworks-sound.mp3"); audio.play(); setTimeout(() => { this.stopFireworks(); }, this.config.duration); }, stopFireworks: function () { if (this._p5Instance) { this._p5Instance.remove(); this._p5Instance = null; } const container = document.getElementById("fireworksContainer"); container.innerHTML = ""; this.fireworksActive = false; if (this.config.disableAllModules) { this.reactivateAndShowModules(); } }, deactivateAndHideModules: function () { const self = this; MM.getModules().enumerate(function (module) { if ( module.name !== "MMM-Fireworks" && self.config.keepModules.indexOf(module.name) === -1 ) { console.log("Hiding module: " + module.name); module.hide(500, () => {}); if (module.suspend) { module.suspend(); } self.disabledModules.push(module); } }); }, reactivateAndShowModules: function () { const self = this; this.disabledModules.forEach(function (module) { console.log("Showing module: " + module.name); module.show(500, () => {}); if (module.resume) { module.resume(); } }); this.disabledModules = []; }, // Integrated p5.js fireworks sketch. initializeP5: function () { if (this._p5Instance) return; const self = this; const config = this.config; this._p5Instance = new p5(function (p) { let fireworks = []; let gravity; // Optional: clear the canvas fully every 2 minutes to remove ghost trails. setInterval(function () { console.log("Performing full redraw (clear) every 2 minutes."); p.clear(); }, 2 * 60 * 1000); p.setup = function () { const container = document.getElementById("fireworksContainer"); p.createCanvas(container.offsetWidth, container.offsetHeight); // Set the canvas opacity to the configured value. p.canvas.style.opacity = config.canvasOpacity; p.colorMode(p.HSB, 255); gravity = p.createVector(0, 0.2); p.background(0); }; p.draw = function () { // Use a semi-transparent background for the trailing effect. p.background(0, 0, 0, config.transparency); // Spawn a new firework with the specified probability. if (p.random(1) < config.fireworkSpawnChance) { fireworks.push(new Firework(p, gravity, config.explosionParticleCount)); } // Update and render fireworks. for (let i = fireworks.length - 1; i >= 0; i--) { fireworks[i].update(); fireworks[i].show(); if (fireworks[i].done()) { fireworks.splice(i, 1); } } }; // --- Particle Class --- class Particle { constructor(p, x, y, hu, isFirework) { this.p = p; this.pos = p.createVector(x, y); this.isFirework = isFirework; if (this.isFirework) { // Rocket's upward velocity based on config values. this.vel = p.createVector(0, p.random(config.magnitude_high, config.magnitude_low)); } else { this.vel = p5.Vector.random2D(); this.vel.mult(p.random(2, 10)); } this.acc = p.createVector(0, 0); this.lifespan = 255; this.hu = hu; } applyForce(force) { this.acc.add(force); } update() { if (!this.isFirework) { this.vel.mult(0.9); this.lifespan -= 4; } this.vel.add(this.acc); this.pos.add(this.vel); this.acc.mult(0); } done() { return this.lifespan < 0; } show() { this.p.strokeWeight(this.isFirework ? 4 : 2); this.p.stroke(this.hu, 255, 255, this.lifespan); this.p.point(this.pos.x, this.pos.y); } } // --- Firework Class --- class Firework { constructor(p, gravity, explosionCount) { this.p = p; this.hu = p.random(255); this.firework = new Particle(p, p.random(p.width), p.height, this.hu, true); this.exploded = false; this.particles = []; this.gravity = gravity; this.explosionCount = explosionCount; }
-
RE: Magic mirror for windows
@sdetweil thank you for the help! it is actually displaying something now. now I can work on getting home assistant installed and anything else i can think of. thank you again for the quick responses and help.