MagicMirror Forum
    • Recent
    • Tags
    • Unsolved
    • Solved
    • MagicMirror² Repository
    • Documentation
    • 3rd-Party-Modules
    • Donate
    • Discord
    • Register
    • Login
    A New Chapter for MagicMirror: The Community Takes the Lead
    Read the statement by Michael Teeuw here.

    Need help building two modules that communicate with each other

    Scheduled Pinned Locked Moved Solved Troubleshooting
    9 Posts 2 Posters 608 Views 2 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D Offline
      DarkwingBuilds
      last edited by

      I’ll try to explain what I’m trying to do as simply as I can, but forgive me as I haven’t coded since college and have had a lot of AI help to get to this point. I’m trying to create two modules. The first is MMM-MealDatabase which is a three column list of items. The items on this list will need display their own unique icon when “unchecked”, and the icon will change to a checkmark when the item is “checked”. The checked or unchecked state of each item is saved locally so that when MagicMirror is exited and restarted, it retains which items were checked or not. This module will be hidden by MMM-Modulebar when not being used to select or de-select the items. The other module MMM-MealList will display a list of whatever items are “checked” in the MMM-MealDatabase module.

      So far, I’ve been able to get the MealDatabase module to display and behave correctly. I can check the items and the state saves after closing MM and restarting. The problem I’m running into, is the MealList module isn’t displaying anything. I am getting the “Sending checked items” log when checking the console. But that’s as far as I’m getting. Posting the code from both .js files below. Hoping someone much smarter than me can take a look and help me out.

      MMM-MealDatabase.js

      Module.register("MMM-MealDatabase", {
        defaults: {
          meals: {
            chicken: ["Chicken Dish 1", "Chicken Dish 2"],
            beef: ["Beef Dish 1", "Beef Dish 2"],
            pork: ["Pork Dish 1", "Pork Dish 2"],
          },
          iconMapping: {
            "Chicken Dish 1": "fa fa-drumstick-bite",
            "Chicken Dish 2": "fa fa-drumstick-bite",
            "Beef Dish 1": "fa fa-cloud-meatball",
            "Beef Dish 2": "fa fa-cloud-meatball",
            "Pork Dish 1": "fa fa-bacon",
            "Pork Dish 2": "fa fa-bacon",
          },
          checkStates: {}, // Ensure that checkStates is initialized as an empty object
        },
      
        getStyles: function () {
          return ["MMM-MealDatabase.css"];
        },
      
        start: function () {
          console.log("MMM-MealDatabase module started");
          this.loaded = false;
          this.loadCheckStates(); // Load check states from local storage
      
          // Send a socket notification to notify other modules about the current checked items
          this.sendCheckedItems();
      
          this.sendSocketNotification("MMM-MEALDATABASE-INIT", this.config.meals);
        },
      
        sendCheckedItems: function () {
          console.log("Sending checked items");
          const checkedItems = Object.keys(this.config.checkStates).filter(meal => this.config.checkStates[meal]);
          this.sendSocketNotification("MMM-MEALDATABASE-CHECKED_ITEMS", checkedItems);
        },
      
        getDom: function () {
          var wrapper = document.createElement("div");
          wrapper.className = "MMM-MealDatabase";
      
          wrapper.appendChild(this.createColumn("Chicken", "chicken"));
          wrapper.appendChild(this.createColumn("Beef", "beef"));
          wrapper.appendChild(this.createColumn("Pork", "pork"));
      
          return wrapper;
        },
      
      createColumn: function (category, mealType) {
        var column = document.createElement("div");
        column.className = "category";
      
        var header = document.createElement("div");
        header.className = "column-header";
        header.appendChild(document.createTextNode(category));
      
        column.appendChild(header);
      
        this.config.meals[mealType].forEach((meal) => {
          var item = document.createElement("div");
          item.className = "item";
      
          var checkbox = document.createElement("input");
          checkbox.type = "checkbox";
          checkbox.id = meal; // Use a unique identifier for each checkbox
          checkbox.className = "checkbox";
      
          // Set the checked state based on the stored state
          checkbox.checked = this.getCheckState(meal);
      
          var label = document.createElement("label");
          label.appendChild(checkbox);
          label.appendChild(document.createTextNode(meal));
      
          item.appendChild(label);
      
          // Add change event to update the checked state
          checkbox.addEventListener("change", () => {
            this.updateCheckState(meal, checkbox.checked);
          });
      
          column.appendChild(item);
        });
      
        return column;
      },
      
        updateCheckState: function (meal, isChecked) {
          // Update the local storage and the module's config
          localStorage.setItem(`MMM-MealDatabase-${meal}`, isChecked ? "checked" : "unchecked");
          this.config.checkStates[meal] = isChecked;
      
          // Send checked items to other modules
          this.sendCheckedItems();
      
          this.updateDom();
        },
      
        getCheckState: function (meal) {
          // Retrieve the check state from local storage
          return localStorage.getItem(`MMM-MealDatabase-${meal}`) === "checked";
        },
      
        loadCheckStates: function () {
          // Load check states from local storage into the module's config
          Object.keys(this.config.meals).forEach((mealType) => {
            this.config.meals[mealType].forEach((meal) => {
              this.config.checkStates[meal] = this.getCheckState(meal);
            });
          });
        },
      });
      

      MMM-MealList.js

      Module.register("MMM-MealList", {
        defaults: {
          checkedItems: [],
          updateInterval: 10000, // Update every 10 seconds (adjust as needed)
        },
      
        getStyles: function () {
          return ["MMM-MealList.css"];
        },
      
        start: function () {
          console.log("MMM-MealList module started");
          this.loaded = false;
          this.sendSocketNotification("MMM-MEALLIST-INIT");
          this.scheduleUpdate();
        },
      
        socketNotificationReceived: function (notification, payload) {
            console.log("Received notification:", notification);
            if (notification === "MMM-MEALDATABASE-CHECKED_ITEMS") {
            this.config.checkedItems = payload;
            this.updateDom();
          }
        },
      
        getDom: function () {
          var wrapper = document.createElement("div");
          wrapper.className = "MMM-MealList";
      
          var header = document.createElement("div");
          header.className = "list-header";
          header.appendChild(document.createTextNode("Checked Items"));
      
          wrapper.appendChild(header);
      
          var list = document.createElement("ul");
          list.className = "checked-list";
      
          this.config.checkedItems.forEach((item) => {
            var listItem = document.createElement("li");
            listItem.appendChild(document.createTextNode(item));
            list.appendChild(listItem);
          });
      
          wrapper.appendChild(list);
      
          return wrapper;
        },
      
        scheduleUpdate: function () {
          var self = this;
          setInterval(function () {
            self.sendSocketNotification("MMM-MEALLIST-UPDATE_REQUEST");
          }, this.config.updateInterval);
        },
      });
      
      S 2 Replies Last reply Reply Quote 0
      • S Offline
        sdetweil @DarkwingBuilds
        last edited by sdetweil

        @DarkwingBuilds said in Need help building two modules that communicate with each other:

        sendCheckedItems

        change this function to use this sendNotification

        this will broadcast this message to all the other modules

        they would listen w this.notificationReceived(topic , data, sender)
        (so you could check if the sending module is yours too)

        and in the list module change this

        socketNotificationReceived
        to
        notificationReceived

        Sam

        How to add modules

        learning how to use browser developers window for css changes

        D 1 Reply Last reply Reply Quote 0
        • S Offline
          sdetweil @DarkwingBuilds
          last edited by sdetweil

          @DarkwingBuilds to get two modules to talk, use this.sendNotifcation() from one with a topic that only the other will know about, and have the second this.sendNotification() back with the data you need(the checked items)

          notice this is NOT sendSocketNotification, which sends down to node_helper

          your modules control the format of the payload, so you can send/receive anything

          the receiver uses the notificationReceived(topic, data) function

          Sam

          How to add modules

          learning how to use browser developers window for css changes

          D 1 Reply Last reply Reply Quote 0
          • D Offline
            DarkwingBuilds @sdetweil
            last edited by

            @sdetweil thank you for the quick reply. I’ll give that a shot and let you know if that fixes it.

            1 Reply Last reply Reply Quote 0
            • S Offline
              sdetweil @DarkwingBuilds
              last edited by sdetweil

              @DarkwingBuilds said in Need help building two modules that communicate with each other:

              sendCheckedItems

              change this function to use this sendNotification

              this will broadcast this message to all the other modules

              they would listen w this.notificationReceived(topic , data, sender)
              (so you could check if the sending module is yours too)

              and in the list module change this

              socketNotificationReceived
              to
              notificationReceived

              Sam

              How to add modules

              learning how to use browser developers window for css changes

              D 1 Reply Last reply Reply Quote 0
              • D Offline
                DarkwingBuilds @sdetweil
                last edited by

                @sdetweil after modifying the send/receive notifications, and checking the console logs, I verified that the Database is sending the correct information and the List is receiving the correct information. Which is much closer than I was before so Thank you! However, it doesn’t look like the List is doing anything with the information. The List module is only displaying a header and nothing else. Updated code is below.

                MMM-MealList.js

                Module.register("MMM-MealList", {
                  defaults: {
                    checkedItems: [],
                    updateInterval: 5000, // Update every 5 seconds (adjust as needed)
                  },
                
                  getStyles: function () {
                    return ["MMM-MealList.css"];
                  },
                
                  start: function () {
                    console.log("MMM-MealList module started");
                    this.loaded = false;
                    this.sendNotification("MMM-MEALLIST-INIT");
                    this.scheduleUpdate();
                  },
                
                  notificationReceived: function (notification, payload) {
                    console.log("Received notification:", notification);
                    if (notification === "MMM-MEALDATABASE-CHECKED_ITEMS") {
                      this.config.checkedItems = payload;
                      this.updateDom();
                    }
                  },
                
                  getDom: function () {
                    var wrapper = document.createElement("div");
                    wrapper.className = "MMM-MealList";
                
                    var header = document.createElement("div");
                    header.className = "list-header";
                    header.appendChild(document.createTextNode("Checked Items"));
                
                    wrapper.appendChild(header);
                
                    var list = document.createElement("ul");
                    list.className = "checked-list";
                
                    this.config.checkedItems.forEach((item) => {
                      var listItem = document.createElement("li");
                      listItem.appendChild(document.createTextNode(item));
                      list.appendChild(listItem);
                    });
                
                    wrapper.appendChild(list);
                
                    return wrapper;
                  },
                
                  scheduleUpdate: function () {
                    var self = this;
                    setInterval(function () {
                      console.log("MMM-MealList update requested");
                      self.sendNotification("MMM-MEALLIST-UPDATE_REQUEST");
                    }, this.config.updateInterval);
                  },
                })
                
                S 1 Reply Last reply Reply Quote 0
                • S Offline
                  sdetweil
                  last edited by sdetweil

                  @DarkwingBuilds always fun!!.

                  now that you are working in the browser side, you can use the developers window debugger and step thru the code

                  ctrl-shift-i on the browser keyboard (you can use the PC browser if u enable mm config.js for outside system access)

                  select to sources tab
                  in the left nav, navigate to modules, your modules, select on the source file name

                  I cropped this just to show you where the debugger tools are and where you would click to add a stop

                  right pointing debugger tool arrow is run to next stop (if any)
                  circle with arrow is step one instruction
                  downward arrow is step into next routine, up arrow is step back out

                  Screenshot at 2023-12-13 09-31-56.jpg

                  and if you use the elements tab, you can see the browser view of your content

                  Sam

                  How to add modules

                  learning how to use browser developers window for css changes

                  S 1 Reply Last reply Reply Quote 0
                  • S Offline
                    sdetweil @DarkwingBuilds
                    last edited by sdetweil

                    @DarkwingBuilds in database,m you are missing the function
                    getCheckedState(meal) and
                    updateCheckState(meal, checked)

                    also, the ‘protocol’ for modules says you should wait til all the modules are loaded before sending them messages

                    see https://docs.magicmirror.builders/development/notifications.html#system-notifications

                    here is the updated database

                    Module.register("MMM-MealDatabase", {
                      defaults: {
                        meals: {
                          chicken: ["Chicken Dish 1", "Chicken Dish 2"],
                          beef: ["Beef Dish 1", "Beef Dish 2"],
                          pork: ["Pork Dish 1", "Pork Dish 2"],
                        },
                        iconMapping: {
                          "Chicken Dish 1": "fa fa-drumstick-bite",
                          "Chicken Dish 2": "fa fa-drumstick-bite",
                          "Beef Dish 1": "fa fa-cloud-meatball",
                          "Beef Dish 2": "fa fa-cloud-meatball",
                          "Pork Dish 1": "fa fa-bacon",
                          "Pork Dish 2": "fa fa-bacon",
                        },
                        checkStates: {}, // Ensure that checkStates is initialized as an empty object
                      },
                      loaded: false,
                      getStyles: function () {
                        return ["MMM-MealDatabase.css"];
                      },
                    
                      start: function () {
                        console.log("MMM-MealDatabase module started");
                        this.loaded = false;
                        //this.loadCheckStates(); // Load check states from local storage
                    
                        // Send a socket notification to notify other modules about the current checked items
                      },
                    
                      sendCheckedItems: function () {
                        console.log("Sending checked items");
                        const checkedItems = Object.keys(this.config.checkStates).filter(meal => this.config.checkStates[meal]);
                        this.sendNotification("MMM-MEALDATABASE-CHECKED_ITEMS", checkedItems);
                      },
                    
                      getDom: function () {
                        var wrapper = document.createElement("div");
                        if(this.loaded){
                          wrapper.className = "MMM-MealDatabase";
                    
                          wrapper.appendChild(this.createColumn("Chicken", "chicken"));
                          wrapper.appendChild(this.createColumn("Beef", "beef"));
                          wrapper.appendChild(this.createColumn("Pork", "pork"));
                        }
                        return wrapper;
                      },
                      notificationReceived(code, payload){
                        if(code ==="MMM-MEALLIST-UPDATE_REQUEST"){
                          this.sendCheckedItems()
                        } else if( code ==="ALL_MODULES_STARTED"){
                        //this.sendNotification("MMM-MEALDATABASE-INIT", this.config.meals);
                        this.sendCheckedItems();
                        this.loaded=true;
                        }
                      },
                    
                      createColumn: function (category, mealType) {
                        var column = document.createElement("div");
                        column.className = "category";
                    
                        var header = document.createElement("div");
                        header.className = "column-header";
                        header.appendChild(document.createTextNode(category));
                    
                        column.appendChild(header);
                    
                        this.config.meals[mealType].forEach((meal) => {
                          var item = document.createElement("div");
                          item.className = "item";
                    
                          var checkbox = document.createElement("input");
                          checkbox.type = "checkbox";
                          checkbox.id = meal; // Use a unique identifier for each checkbox
                          checkbox.className = "checkbox";
                    
                          // Set the checked state based on the stored state
                          checkbox.checked = this.getCheckState(meal);
                          this.updateCheckState(meal, checkbox.checked);
                          var label = document.createElement("label");
                          label.appendChild(checkbox);
                          label.appendChild(document.createTextNode(meal));
                    
                          item.appendChild(label);
                    
                          // Add change event to update the checked state
                          checkbox.addEventListener("change", () => {
                            this.updateCheckState(meal, checkbox.checked);
                          });
                    
                          column.appendChild(item);
                        });
                    
                        return column;
                      },
                      getCheckState(meal){
                        let checked=false
                        try {
                         checked = this.config.checkStates[meal].checked
                        }
                        catch(err){}
                        return checked
                      },
                      updateCheckState(meal, checked){
                        if(this.config.checkStates[meal] == undefined)
                          this.config.checkStates[meal]={}
                        this.config.checkStates[meal].checked=checked
                      }
                    })
                    

                    still looking at list

                    Sam

                    How to add modules

                    learning how to use browser developers window for css changes

                    1 Reply Last reply Reply Quote 0
                    • S Offline
                      sdetweil @sdetweil
                      last edited by

                      @DarkwingBuilds
                      and here is list

                      Module.register("MMM-MealList", {
                        defaults: {
                          checkedItems: [],
                          updateInterval: 10000, // Update every 10 seconds (adjust as needed)
                        },
                      
                        getStyles: function () {
                          return ["MMM-MealList.css"];
                        },
                      
                        start: function () {
                          console.log("MMM-MealList module started");
                          this.loaded = false;
                          this.sendNotification("MMM-MEALLIST-INIT");
                          this.scheduleUpdate();
                        },
                      
                        notificationReceived: function (notification, payload) {
                            console.log("Received notification:", notification);
                            if (notification === "MMM-MEALDATABASE-CHECKED_ITEMS") {
                            this.config.checkedItems = payload;
                            this.updateDom();
                          }
                        },
                      
                        getDom: function () {
                          var wrapper = document.createElement("div");
                          wrapper.className = "MMM-MealList";
                      
                          var header = document.createElement("div");
                          header.className = "list-header";
                          header.appendChild(document.createTextNode("Checked Items"));
                      
                          wrapper.appendChild(header);
                      
                          var list = document.createElement("ul");
                          list.className = "checked-list";
                      
                          this.config.checkedItems.forEach((item) => {
                            var listItem = document.createElement("li");
                            listItem.appendChild(document.createTextNode(item));
                            list.appendChild(listItem);
                          });
                      
                          wrapper.appendChild(list);
                      
                          return wrapper;
                        },
                      
                        scheduleUpdate: function () {
                          var self = this;
                          setInterval(function () {
                            self.sendNotification("MMM-MEALLIST-UPDATE_REQUEST");
                          }, this.config.updateInterval);
                        },
                      });
                      

                      Sam

                      How to add modules

                      learning how to use browser developers window for css changes

                      S 1 Reply Last reply Reply Quote 0
                      • S Offline
                        sdetweil @sdetweil
                        last edited by

                        @DarkwingBuilds both showing (checked needs to be looked at)
                        Screenshot at 2023-12-13 11-07-43.png

                        Sam

                        How to add modules

                        learning how to use browser developers window for css changes

                        1 Reply Last reply Reply Quote 0
                        • 1 / 1
                        • First post
                          Last post
                        Enjoying MagicMirror? Please consider a donation!
                        MagicMirror created by Michael Teeuw.
                        Forum managed by Sam, technical setup by Karsten.
                        This forum is using NodeBB as its core | Contributors
                        Contact | Privacy Policy