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.

    MMM-FroniusSolar family modules

    Scheduled Pinned Locked Moved Utilities
    31 Posts 5 Posters 6.5k Views 5 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.
    • C Offline
      chrisfr1976 @waynerob11
      last edited by

      @waynerob11 Hello,
      I’ve created a version with console.logs inside. Go to the MMM-FroniusSolar2 module folder and replace the files with this content:

      MMM-FroniusSolar2.js:

      Module.register("MMM-FroniusSolar2", {
          defaults: {
              InverterIP: "192.168.178.134",
              updateInterval: 60000, // Update every 60 seconds
              icons: {
                  P_Akku: "mdi:car-battery",
                  P_Grid: "mdi:transmission-tower",
                  P_Load: "mdi:home-lightbulb",
                  P_PV: "mdi:solar-panel-large",
              },
              Radius: 80, // Radius for the SVG gauges
              MaxPower: 1000, // Maximum power for grid, house, and battery
              MaxPowerPV: 10400, // Maximum power for solar PV
              ShowText: true,
              TextMessge: [
                  { about: "600", Text: "Leicht erhöhter Netzbezug.", color: "#999" },
                  { about: "1000", Text: "Über 1 KW Netzbezug!", color: "#ffffff" },
                  { about: "1500", Text: "Über 1,5KW Netzbezug.", color: "#eea205" },
                  { about: "2500", Text: "Über 2,5KW aus dem Netz!", color: "#ec7c25" },
                  { about: "5000", Text: "Auto lädt, richtig? Nächstes Mal auf Sonne warten.", color: "#cc0605" },
                  { less: "-500", Text: "Sonne scheint! Mehr als 500W frei.", color: "#f8f32b" },
                  { less: "-2000", Text: "Wäsche waschen! Über 2KW freie Energie!", color: "#00bb2d" },
                  { less: "-4000", Text: "Auto laden! Über 4KW freie Energie!", color: "#f80000" },
              ],
              debug: true, // Set to true to enable debug logging
          },
      
          start: function () {
              if (this.config.debug) {
                  console.log("[MMM-FroniusSolar2] Starting module with config:", this.config);
              }
              this.solarData = null;
              this.solarSOC = null; // Added for SOC
              console.log("[MMM-FroniusSolar2] Sending configuration to node_helper...");
              this.sendSocketNotification("SET_CONFIG", this.config); // Send configuration to node_helper
              this.scheduleUpdate(); // Schedule periodic updates
          },
      
          getStyles: function () {
              return ["MMM-FroniusSolar2.css", "https://code.iconify.design/2/2.2.1/iconify.min.js"];
          },
      
          scheduleUpdate: function () {
              if (this.config.debug) {
                  console.log("[MMM-FroniusSolar2] Scheduling updates every", this.config.updateInterval, "ms");
              }
              const self = this;
              setInterval(function () {
                  if (self.config.debug) {
                      console.log("[MMM-FroniusSolar2] Requesting data update from node_helper...");
                  }
                  self.sendSocketNotification("GET_FRONIUS_DATA");
              }, this.config.updateInterval);
          },
      
          socketNotificationReceived: function (notification, payload) {
              if (notification === "FRONIUS_DATA") {
                  if (this.config.debug) {
                      console.log("[MMM-FroniusSolar2] Received FRONIUS_DATA:", payload);
                  }
                  this.solarData = {
                      P_Akku: Math.round(payload.P_Akku || 0),
                      P_Grid: Math.round(payload.P_Grid || 0),
                      P_Load: Math.round(payload.P_Load || 0),
                      P_PV: Math.round(payload.P_PV || 0),
                  };
                  this.solarSOC =
                      payload.Inverters &&
                      payload.Inverters["1"] &&
                      payload.Inverters["1"].SOC
                          ? Math.round(payload.Inverters["1"].SOC)
                          : 0; // Fallback to 0 if SOC is undefined
                  if (this.config.debug) {
                      console.log("[MMM-FroniusSolar2] Processed solarData:", this.solarData, "SOC:", this.solarSOC);
                  }
                  this.updateDom();
              }
          },
      
          getDom: function () {
              if (this.config.debug) {
                  console.log("[MMM-FroniusSolar2] Rendering DOM");
              }
              const wrapper = document.createElement("div");
              wrapper.className = "solar2-wrapper";
      
              if (!this.solarData) {
                  wrapper.innerHTML = "Loading...";
                  return wrapper;
              }
      
              const radius = this.config.Radius || 80;
              const strokeWidth = 12;
              const svgSize = 350; // Adjusted SVG height for labels and padding
      
              // Recalculate house consumption
              const outerPower = this.solarData.P_Grid + this.solarData.P_Akku + this.solarData.P_PV;
      
              // Fixed positions for the gauges in dice layout
              const positions = {
                  PV: { x: 75, y: 75 },
                  Grid: { x: 225, y: 75 },
                  Akku: { x: 75, y: 225 },
                  House: { x: 225, y: 225 }
              };
      
              // Define colors for gauges
              const gridColor = this.solarData.P_Grid >= 0 ? "#808080" : "#add8e6"; // Gray for positive, light blue for negative
              const akkuColor = "#00ff00"; // Always green
              const pvColor = "#ffff00"; // Always yellow
      
              // House Gauge: Logic for color determination
              let houseColor;
              if (this.solarData.P_Akku - 100 > Math.abs(this.solarData.P_Grid)) {
                  houseColor = "#a3c49f"; // Light green for high battery activity
              } else if (this.solarData.P_Grid > 150) {
                  houseColor = "#808080"; // Gray for high grid consumption
              } else if (outerPower > 0) {
                  houseColor = "#00ff00"; // Green for positive power flow
              } else {
                  houseColor = "#1f84ff"; // Light blue
              }
      
              if (this.config.debug) {
                  console.log("[MMM-FroniusSolar2] Gauge colors determined:",
                      { pvColor, gridColor, akkuColor, houseColor });
              }
      
              // Create unified SVG
              const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
              svg.setAttribute("width", svgSize);
              svg.setAttribute("height", svgSize);
              svg.setAttribute("viewBox", "0 -20 300 350"); // Adjusted viewBox to fix label cutoff
              svg.style.margin = "auto"; // Center the SVG
      
              // Define glow effect
              const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
              defs.innerHTML = `
                  <filter id="glow">
                      <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blurred" />
                      <feMerge>
                          <feMergeNode in="blurred" />
                          <feMergeNode in="SourceGraphic" />
                      </feMerge>
                  </filter>
              `;
              svg.appendChild(defs);
      
              // Function to create a line
              const createLine = (x1, y1, x2, y2, color) => {
                  if (this.config.debug) {
                      console.log("[MMM-FroniusSolar2] Creating line:", { x1, y1, x2, y2, color });
                  }
                  const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
                  line.setAttribute("x1", x1);
                  line.setAttribute("y1", y1);
                  line.setAttribute("x2", x2);
                  line.setAttribute("y2", y2);
                  line.setAttribute("stroke", color);
                  line.setAttribute("stroke-width", "4");
                  line.setAttribute("class", "flow-lines");
                  return line;
              };
      
              // Add flow lines based on power conditions
              if (this.solarData.P_Akku < -10 && this.solarData.P_PV > 0 ) {
                  svg.appendChild(createLine(positions.PV.x, positions.PV.y + radius, positions.Akku.x, positions.Akku.y - radius, "#ffff00"));
              }
              if (this.solarData.P_PV > 10) {
                  svg.appendChild(createLine(positions.PV.x + (radius * 0.7071), positions.PV.y + (radius * 0.7071), positions.House.x - (radius * 0.7071), positions.House.y - (radius * 0.7071), "#ffff00"));
              }
              if (this.solarData.P_Grid > 10) {
                  svg.appendChild(createLine(positions.Grid.x, positions.Grid.y + radius, positions.House.x, positions.House.y - radius, "#808080"));
              }
              if (this.solarData.P_Akku > 10) {
                  svg.appendChild(createLine(positions.Akku.x + radius, positions.Akku.y, positions.House.x - radius, positions.House.y, "#00ff00"));
              }
              if (this.solarData.P_Grid < -10 && this.solarData.P_PV > Math.abs(this.solarData.P_Grid)) {
                  svg.appendChild(createLine(positions.PV.x + radius, positions.PV.y, positions.Grid.x - radius, positions.Grid.y, "#add8e6"));
              }
              if (this.solarData.P_Grid < -10 && this.solarData.P_PV <= 0) {
                  svg.appendChild(createLine(positions.Akku.x + (radius * 0.7071), positions.Akku.y - (radius * 0.7071), positions.Grid.x - (radius * 0.7071), positions.Grid.y + (radius * 0.7071), "#00ff00"));
              }
              if (this.solarData.P_PV <= 0 && this.solarData.P_Akku < -10) {
                  svg.appendChild(createLine(positions.Grid.x - (radius * 0.7071), positions.Grid.y + (radius * 0.7071), positions.Akku.x + (radius * 0.7071), positions.Akku.y - (radius * 0.7071), "#808080"));
              }
      
              // Function to create a gauge
              const createGauge = (x, y, mainValue, subValue, percentage, color, label, icon, labelPosition) => {
                  if (this.config.debug) {
                      console.log("[MMM-FroniusSolar2] Creating gauge:", { label, mainValue, subValue, percentage, color });
                  }
                  const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
                  group.setAttribute("transform", `translate(${x},${y})`);
      
                  // Circle Background
                  const bgCircle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
                  bgCircle.setAttribute("cx", 0);
                  bgCircle.setAttribute("cy", 0);
                  bgCircle.setAttribute("r", radius);
                  bgCircle.setAttribute("stroke", "#e0e0e0");
                  bgCircle.setAttribute("opacity", "1");
                  bgCircle.setAttribute("stroke-width", strokeWidth);
                  bgCircle.setAttribute("fill", "none");
                  group.appendChild(bgCircle);
      
                  // Circle Progress with glow
                  const progressCircle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
                  progressCircle.setAttribute("cx", 0);
                  progressCircle.setAttribute("cy", 0);
                  progressCircle.setAttribute("r", radius);
                  progressCircle.setAttribute("stroke", color);
                  progressCircle.setAttribute("stroke-width", strokeWidth);
                  progressCircle.setAttribute("fill", "none");
                  progressCircle.setAttribute("stroke-dasharray", `${percentage * 2 * Math.PI * radius} ${2 * Math.PI * radius}`);
                  progressCircle.setAttribute("transform", "rotate(-90 0 0)");
                  progressCircle.setAttribute("filter", "url(#glow)"); // Apply glow filter
                  group.appendChild(progressCircle);
      
                  // Main Text
                  const mainText = document.createElementNS("http://www.w3.org/2000/svg", "text");
                  mainText.setAttribute("x", 0);
                  mainText.setAttribute("y", 6); // Adjusted y position for main text
                  mainText.setAttribute("text-anchor", "middle");
                  mainText.setAttribute("font-size", "22px");
                  mainText.setAttribute("fill", "#ffffff");
                  mainText.textContent = mainValue;
                  group.appendChild(mainText);
      
                  // Sub Text
                  if (subValue) {
                      const subText = document.createElementNS("http://www.w3.org/2000/svg", "text");
                      subText.setAttribute("x", 0);
                      subText.setAttribute("y", 25); // Adjusted y position for sub text
                      subText.setAttribute("text-anchor", "middle");
                      subText.setAttribute("font-size", "16px");
                      subText.setAttribute("fill", "#ffffff");
                      subText.textContent = subValue;
                      group.appendChild(subText);
                  }
      
                  // Label with foreignObject for icon
                  const labelY = labelPosition === "top" ? -(radius + 45) : radius + 15; // Increased offset for top labels
                  const labelContainer = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
                  labelContainer.setAttribute("x", -radius - 20);
                  labelContainer.setAttribute("y", labelY);
                  labelContainer.setAttribute("text-anchor", "middle");
                  labelContainer.setAttribute("width", radius * 2);
                  labelContainer.setAttribute("height", labelPosition === "top" ? 60 : 40); // Adjusted label height
                  labelContainer.innerHTML = `
                      <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: center; justify-content: center; text-align: center; font-size: 20px; color: white;">
                          <span class="iconify" data-icon="${icon}" style="margin-right: 5px;"></span>
                          ${label}
                      </div>
                  `;
                  group.appendChild(labelContainer);
      
                  return group;
              };
      
              // Add gauges to SVG
              svg.appendChild(createGauge(
                  positions.PV.x,
                  positions.PV.y,
                  `${this.solarData.P_PV || 0} W`,
                  null,
                  Math.min((this.solarData.P_PV || 0) / this.config.MaxPowerPV, 1),
                  pvColor,
                  "PV",
                  this.config.icons.P_PV,
                  "top"
              ));
              svg.appendChild(createGauge(
                  positions.Grid.x,
                  positions.Grid.y,
                  `${this.solarData.P_Grid || 0} W`,
                  null,
                  Math.min(Math.abs(this.solarData.P_Grid || 0) / this.config.MaxPower, 1),
                  gridColor,
                  "Grid",
                  this.config.icons.P_Grid,
                  "top"
              ));
              svg.appendChild(createGauge(
                  positions.Akku.x,
                  positions.Akku.y,
                  `${this.solarSOC || 0}%`,
                  `${this.solarData.P_Akku || 0} W`,
                  Math.min(this.solarSOC / 100, 1),
                  akkuColor,
                  "Akku",
                  this.config.icons.P_Akku,
                  "bottom"
              ));
              svg.appendChild(createGauge(
                  positions.House.x,
                  positions.House.y,
                  `${outerPower || 0} W`,
                  null,
                  Math.min(Math.abs(outerPower || 0) / this.config.MaxPower, 1),
                  houseColor,
                  "House",
                  this.config.icons.P_Load,
                  "bottom"
              ));
      
              wrapper.appendChild(svg);
      
              // Add dynamic text message below the gauge
              if (this.config.ShowText) {
                  const textMessageDiv = document.createElement("div");
                  textMessageDiv.className = "text-message2";
      
                  const messageConfig = this.config.TextMessge || [];
                  let selectedMessage = null;
      
                  for (const message of messageConfig) {
                      if (
                          (message.about && this.solarData.P_Grid > parseInt(message.about)) ||
                          (message.less && this.solarData.P_Grid < parseInt(message.less))
                      ) {
                          // If no message is selected yet, or the new match is more specific
                          if (
                              !selectedMessage ||
                              (message.about && parseInt(message.about) > parseInt(selectedMessage.about || -Infinity)) ||
                              (message.less && parseInt(message.less) < parseInt(selectedMessage.less || Infinity))
                          ) {
                              selectedMessage = message;
                          }
                      }
                  }
      
                  if (selectedMessage) {
                      if (this.config.debug) {
                          console.log("[MMM-FroniusSolar2] Selected text message:", selectedMessage);
                      }
                      textMessageDiv.innerHTML = `
                          <span style="color: ${selectedMessage.color}; font-size: 18px;">
                              ${selectedMessage.Text}
                          </span>
                      `;
                  } else {
                      textMessageDiv.innerHTML = `
                          <span style="color: #999; font-size: 16px;">
                              PV Anlage läuft...
                          </span>
                      `;
                  }
      
                  wrapper.appendChild(textMessageDiv);
              }
      
              return wrapper;
          }
      });
      
      

      node_helper.js::

      const NodeHelper = require("node_helper");
      
      module.exports = NodeHelper.create({
          config: null, // Initially, no config is set
      
          start: function () {
              console.log("[MMM-FroniusSolar2] Node helper started");
          },
      
          socketNotificationReceived: function (notification, payload) {
              console.log("[MMM-FroniusSolar2] Received socket notification:", notification, payload);
              if (notification === "SET_CONFIG") {
                  this.config = payload;
                  if (this.config.debug) {
                      console.log("[MMM-FroniusSolar2] Configuration received:", this.config);
                  }
                  this.startFetchingData();
              } else if (notification === "GET_FRONIUS_DATA") {
                  if (!this.config) return;
                  if (this.config.debug) {
                      console.log("[MMM-FroniusSolar2] GET_FRONIUS_DATA triggered");
                  }
                  this.getFroniusData();
              }
          },
      
          startFetchingData: function () {
              if (this.config && this.config.InverterIP) {
                  if (this.config.debug) {
                      console.log("[MMM-FroniusSolar2] Starting data fetch interval for IP", this.config.InverterIP);
                  }
                  this.fetchInterval = setInterval(() => {
                      this.getFroniusData();
                  }, 60000); // Fetch data every 60 seconds
              }
          },
      
          getFroniusData: function () {
              if (!this.config || !this.config.InverterIP) return;
      
              const url = `http://${this.config.InverterIP}/solar_api/v1/GetPowerFlowRealtimeData.fcgi`;
              if (this.config.debug) {
                  console.log("[MMM-FroniusSolar2] Fetching data from URL:", url);
              }
      
              fetch(url)
                  .then(response => {
                      if (this.config.debug) {
                          console.log("[MMM-FroniusSolar2] Response received:", response);
                      }
                      if (!response.ok) {
                          throw new Error(`HTTP error!`);
                      }
                      return response.json();
                  })
                  .then(data => {
                      if (this.config.debug) {
                          console.log("[MMM-FroniusSolar2] JSON data received:", data);
                      }
                      const siteData = data.Body.Data.Site;
                      const inverterData = data.Body.Data.Inverters ? data.Body.Data.Inverters["1"] : {};
      
                      const result = {
                          P_Akku: siteData.P_Akku || 0,
                          P_Grid: siteData.P_Grid || 0,
                          P_Load: siteData.P_Load || 0,
                          P_PV: siteData.P_PV || 0,
                          Inverters: { "1": { SOC: inverterData.SOC || 0 } },
                      };
      
                      if (this.config.debug) {
                          console.log("[MMM-FroniusSolar2] Parsed result:", result);
                      }
      
                      this.sendSocketNotification("FRONIUS_DATA", result);
                  })
                  .catch(error => {
                      console.error("[MMM-FroniusSolar2] Error fetching data:", error);
                  });
          },
      });
      
      

      This will create a lot of log entry. So only use this temporary. If you see any errors you can’t solve, please send me the logs.

      Later use pm2 flush to clear the logs. The log-file size will increase with this version quite fast.

      Regards, Chris.

      1 Reply Last reply Reply Quote 0
      • C Offline
        chrisfr1976 @waynerob11
        last edited by

        @waynerob11
        Hi,
        is this issue solved now?

        Regards, Chris.

        1 Reply Last reply Reply Quote 0
        • T Offline
          TomOMaley
          last edited by

          Hi, does meanwhil one of your fronius modules work whithout Problems? Tried the second one but no display at all. What changes in the config etc. are still necessary?
          Regards Walter

          C 1 Reply Last reply Reply Quote 0
          • C Offline
            chrisfr1976 @TomOMaley
            last edited by chrisfr1976

            @TomOMaley
            All modules work without any problems on my mirror. No errors, nothing in the logs and it gets data every 2 seconds.

            To go to the chat and post a mimimi only is not that helpful to find the error. Use the log-version above then you‘ll find your error.

            Read the doc, install the latest module, enter a correct ip and activate the api in your inverter. I cannot see any more I can do here. At least post an error log or somerhing helpful.

            Regards, Chris.

            T 1 Reply Last reply Reply Quote 0
            • T Offline
              TomOMaley @chrisfr1976
              last edited by

              @chrisfr1976 Dear Chris, I apologize for my unqualified question. Regards

              1 Reply Last reply Reply Quote 0
              • W Offline
                waynerob11 @waynerob11
                last edited by

                Hi Chris,

                Don’t know what I did but it’s all working now - only problem is all the circles are bunched up.
                Also, how can I enlarge and bold the fonts

                @waynerob11

                C W 2 Replies Last reply Reply Quote 0
                • C Offline
                  chrisfr1976 @waynerob11
                  last edited by

                  @waynerob11
                  Hi,
                  please send a screenshot to understand clearly what you like to do.

                  Regards, Chris.

                  1 Reply Last reply Reply Quote 0
                  • W Offline
                    waynerob11 @waynerob11
                    last edited by

                    @waynerob11 IMG_20250225_121645_713.jpg

                    C 1 Reply Last reply Reply Quote 0
                    • C Offline
                      chrisfr1976 @waynerob11
                      last edited by

                      @waynerob11 Hi,


                      please check in your config.js the radius value. It should be Radius: 50,. In the beginning when I released the module there was a copy+paste error. The radius was 80.


                      If you want to enlarge everything as I see the gauge radius on your mirror you need do go deeper into the MMM-FroniusSolar2.js file:

                      • Adjust text size in // Main Text | // Sub Text area.
                      • Modify gauge coordinates here:
                              const positions = {
                                  PV: { x: 75, y: 75 },
                                  Grid: { x: 225, y: 75 },
                                  Akku: { x: 75, y: 225 },
                                  House: { x: 225, y: 225 }
                              };
                      
                      • If everything is bigger modify also the view box:
                                svg.setAttribute("viewBox", "0 -20 300 350"); // Adjusted viewBox to fix label cutoff
                      

                      The view box part is really annoying. It does not what you expect (The gauges do not have 50px. In fact everything is scaled in the viewbox). If you have a working status which might be useful for others, please share. I cannot support here since it is really tricky.

                      Regards, Chris.

                      W 1 Reply Last reply Reply Quote 0
                      • W Offline
                        waynerob11 @chrisfr1976
                        last edited by

                        @chrisfr1976 Changing it from 80 to 50 fixed the problem - thankyou

                        C 1 Reply Last reply Reply Quote 0
                        • 1
                        • 2
                        • 3
                        • 4
                        • 3 / 4
                        • 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