Temperaturverteilung im Haus visualisieren

Einklappen
X
 
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge
  • MarkusCosi
    LoxBus Spammer
    • 28.09.2023
    • 321

    #1

    Temperaturverteilung im Haus visualisieren

    Hallo zusammen,

    da man ja alsbald eine Menge an Sensorik und insbesondere Temperaturfühler im Haus hat, so wird es doch recht unübersichtlich wenn man mal auf einen Blick herausfinden möchte wie es um die Temperaturen im Haus bestellt ist. z.B. welches Zimmer ist kalt? welches im Verhältnis zu den anderen warm? Wie sieht also die räumliche Temperaturverteilung im Haus aus?

    Das scheint sich schonmal jemand gedacht zu haben: https://medium.com/visumd/roomcanvas...s-273fb825825f

    "RoomCanvas" heißt das Projekt. Das Konferenz-Paper dazu ist im Artikel unten verlinkt und gibt weitere Beispiele. Die Demo die ma dazu findet ist leider nicht mehr abrufbar, und auch sonst passiert da leider scheinbar nichts. Cool ist natürlich die Interpolation noch dabei.

    Das ist nun natürlich etwas overkill, aber eine wie auch immer geartete grafische Anzeige der relativen Temperaturverteilung im Haus wäre nett.

    Aktuell nutze ich ein Schema mit dem Grundriss des Hauses und den Temperaturen & Luftfeuchten als Werte drüber gelegt. Man könnte mit dem Mittelwert des Hauses und Statusbausteinen noch etwas Farbe (codiert zu den relativ-Werten) reinbringen. Aber so richtig schön fände ich das noch immer nicht. Es wäre ja nur farbiger Text...

    Hat vllt. schonmal jemand eine lokale homepage / server eingerichtet und mit html farbige Patches o.Ä. über einen Grundriss gelegt um etwas zu visualisieren?

    Danke!
  • Lenardo
    MS Profi
    • 25.08.2015
    • 641

    #2
    du könntest aber auch ganz simpel Grafana (Loxberry Plugin) nutzen, wenn du die Raumbezeichnungen ein wenig gruppierst gibts vielleicht eine noch bessere Übersicht

    Klicke auf die Grafik für eine vergrößerte Ansicht  Name: Bildschirmfoto 2024-09-24 um 14.22.02.png Ansichten: 0 Größe: 119,2 KB ID: 442021

    Kommentar


    • MarkusCosi
      MarkusCosi kommentierte
      Kommentar bearbeiten
      hallo, danke für die Idee. Tatsächlich steht Grafana und Statistiken per Loxberry schon eine Weile auf meiner Liste. Aber die räumliche Verteilung von Messgrößen gibt das ja leider trotzdem nicht her ... Aber schick und schon etwas übersichtrlicher sieht das natürlich trotzdem aus!
  • MarkusCosi
    LoxBus Spammer
    • 28.09.2023
    • 321

    #3
    Hallo zusammen,

    ich habe nun eine html-page gebaut die von Loxone per json-API die Temperatur-Daten abgreift. Dazu habe ich in der Loxone Config einen Benutzer mit LoxoneUsername:LoxoneUserPW eingerichtet und für die Temperaturen (unter Funktionen) berechtigt. Vorgegangen bin ich grundsätzlich ähnlich wie hier, allerdings habe ich die javascript Sektion etwas angepasst um die Farbcodierung hinzubekommen:

    In dem index.html file (bearbeitet mit dem Texteditor (plain text) von MacOS) habe ich eine Bild-Datei vom Grundriss als Hintergrund gewählt (vorher mal per Safari geöffnet um den Pfad zu bekommen). Dann habe ich transparente Rechtecke erstellt und farbcodiere die beim Abrufen der Temp-Daten. Das Platzieren ist die manuelle und friemelige Arbeit gewesen. Auch das Auslesen der uuids vom ReportDeviceStatus.txt der Loxone Config (siehe Video oben) war etwas schwierig, da nicht alle Temp-Ausgänge / Stati sinnvolle Namen hatten. Gefunden habe ich die dann über die uuids der Räume und die Einheit (bei mir "°", deshalb auch der substring-Befehl zum rauskürzen der Einheit zum nachfolgenden Rechnen mit der Zahl).

    Die Farbcodierung geschieht mittels color-mixing und den Temperaturdaten relativ zu einer "normalen" Comfort-Temperatur.

    Es sieht schon ganz schick aus, aber aktuell kann ich noch nicht mehr als 6 Daten abrufen ohne zwischendurch eine Pause einzulegen. An sonsten kommt eine "access limit" Nachricht bzw. Fehlermeldung. Kann man das bei der Loxone API irgendwo einstellen? Aber so funktioniert es auch erst einmal schon...

    Kann mir außerdem jemand helfen und würde mir verraten wo man am besten die html-Datei auf dem Loxberry ablegt?
    Edit: Auf dem Loxberry habe ich über den filemanager die index.html Datei unter "/opt/loxberry/webfrontend/legacy/TempDistribution" in das dort angelegte neue Verzeichnis "TempDistribution" hochgeladen. In den selben Ordner den Grundriss "Plan.jpg" als jpg. vgl. hier.

    In der Loxone Config habe ich dann einen Baustein "Weblink" angelegt und auf "http://<<IPLoxberry>>/legacy/TempDistribution/index.html" verwiesen. So läuft das jetzt und zeigt die Temperaturverteilung im Haus grafisch an...

    Danke!

    -------------------

    <!DOCTYPE html>
    <html>
    <head>
    <title>API Data Example</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
    body {
    background-color: #222;
    color: white;
    font-family: Arial, sans-serif;
    text-align: center;
    }

    .image-container {
    background-image: url("http://<<IPLoxberry>>/legacy/TempDistribution/Plan.jpg");
    background-size: contain;
    background-repeat: no-repeat;
    background-position: top left;
    position: relative;
    width: 100%;
    height: 0;
    padding-bottom: 100%;
    }

    </style>
    </head>
    <body>
    <main>
    <div class="image-container">
    <div id="rect_WZ" style="width:38.5%; height:15.6%; position: absolute; top:3.8%; left: 2.75%;"></div>
    <div id="rect_HausGarage" style="width:10.8%; height:27.0%; position: absolute; top:36.2%; left: 29.9%;"></div>
    <div id="rect_Bad" style="width:22%; height:12%; position: absolute; top: 35.7%; left: 74.7%;"></div>
    ...
    <div id="rect_Außen1" style="width:100%; height:1.8%; position: absolute; top: 65%; left: 0%;"></div>
    ..
    <div id="rect_Außen10" style="width:39.7%; height:1%; position: absolute; top: 64%; left: 1.8%;"></div>
    </div>
    </main>
    <script>
    //Loxone uuid's for Temperature values:
    const temperatureURL_Außen = 'http://<<IP Miniserver>>/jdev/sps/io/1c557d59-XXX-XXX-ffff4865a4527a1d/state';
    const temperatureURL_WZ = 'http://<<IP Miniserver>>/jdev/sps/io/1c557d59-XXX-XXX-ffff4865a4527a1d/state';
    const temperatureURL_HausGarage = 'http://<<IP Miniserver>>/jdev/sps/io/1d67be32-XXXX-XXX-ffff4865a4527a1d/state';
    const temperatureURL_Bad = 'http://<<IP Miniserver>>/jdev/sps/io/1c159b7c-XXXX-XXXX-ffff4865a4527a1d/state';

    const TComf = 23;

    // Encode the username and password for basic authentication, set user in Loxone Config & authorize on Temp data
    const authString = btoa('LoxoneUsername:LoxoneUserPW');

    function fetchData(url, container, rect, TComf) {
    fetch(url, {
    headers: {
    'Authorization': 'Basic ' + authString
    }
    })
    .then(response => response.json())
    .then(data => {
    // Extract the desired value from the JSON response and update the data on the screen
    const value1 = data.LL.value;
    const value = value1.substring(0, value1.length - 1)
    const currentValue = parseFloat(container.textContent);
    if (currentValue !== value) {
    container.textContent = value;
    container.style.fontSize = "x-small";
    container.style.color = "white";
    container.style.textAlign = "center";
    container.style.fontStyle = "italic";
    container.style.fontWeight = "normal"
    for (let i = 0; i < rect.length; i++) {
    document.getElementById(rect[i]).style.backgroundColor=colorMixer([255,0,0], [0,0,255], 0.5+2*(value-TComf)/TComf);
    //alternatively: rgb(0, 0, (value/Avg)*255);
    document.getElementById(rect[i]).style.opacity=0.4;
    }
    container.style.animation = 'pulse 1s';
    setTimeout(() => {
    container.style.animation = '';
    }, 1000);
    }
    })
    .catch(error => console.error('Error fetching data:', error));
    }

    function FetchData() {
    FirstHeap();
    setTimeout(SecondHeap, 5000);
    setTimeout(ThirdHeap, 10000);
    setTimeout(FourthHeap, 20000);
    }​

    function FirstHeap() {
    fetchData(temperatureURL_Außen, rect_Außen2, ['rect_Außen1', ..., 'rect_Außen10'], TComf);
    fetchData(temperatureURL_WZ, document.getElementById('rect_EGWZ1'), ['rect_EGWZ1','rect_EGWZ2'], TComf);
    ...
    ... <<max 6 mal!>>
    }
    function SecondHeap() {
    fetchData(temperatureURL_HausGarage, document.getElementById('rect_HausGarage'), ['rect_HausGarage'], TComf);
    ... <<max 6 mal!>>
    }
    function ThirdHeap() {
    ... <<max 6 mal!>>
    }​
    function FourthHeap() {
    ... <<max 6 mal!>>
    }​

    // Fetch the initial data when the page loads
    FetchData();

    // Schedule updates every 60 seconds
    setInterval(() => {
    FetchData();
    }, 60000);​

    //some color functions for java
    dec2hex = function (d) {
    if (d > 15)
    { return d.toString(16) } else
    { return "0" + d.toString(16) }
    }
    rgb = function (r, g, b) { return "#" + dec2hex(r) + dec2hex(g) + dec2hex(b) };

    //colorChannelA and colorChannelB are ints ranging from 0 to 255
    function colorChannelMixer(colorChannelA, colorChannelB, amountToMix){
    var channelA = colorChannelA*amountToMix;
    var channelB = colorChannelB*(1-amountToMix);
    return parseInt(channelA+channelB);
    }

    //rgbA and rgbB are arrays, amountToMix ranges from 0.0 to 1.0
    //example (red): rgbA = [255,0,0]
    function colorMixer(rgbA, rgbB, amountToMix){
    var r = colorChannelMixer(rgbA[0],rgbB[0],amountToMix);
    var g = colorChannelMixer(rgbA[1],rgbB[1],amountToMix);
    var b = colorChannelMixer(rgbA[2],rgbB[2],amountToMix);
    return "rgb("+r+","+g+","+b+")";
    }
    </script>
    </body>
    </html>
    Zuletzt geändert von MarkusCosi; 26.09.2024, 13:27.

    Kommentar

    • MarkusCosi
      LoxBus Spammer
      • 28.09.2023
      • 321

      #4
      ...alternativ mit einem Farbschema wie im Paper...
      dazu ersetzen wie folgt:
      document.getElementById(rect[i]).style.backgroundColor=colorMixer([255,0,0], [0,0,255], 0.5+2*(value-TComf)/TComf);
      → document.getElementById(rect[i]).style.backgroundColor=colorScheme(0.5+2*(value-TComf)/TComf);

      und folgende Funktion einfügen...
      //color scheme as a function
      function colorScheme(p){
      const col1 = [54, 79, 201]; //dark blue
      const col2 = [106, 171, 212]; //darker blue
      const col3 = [197, 223, 240]; //light blue
      const col4 = [237, 210, 209]; //light red
      const col5 = [222, 167, 138]; //light orange
      const col6 = [122, 43, 39]; //dark red
      if (p < 0.0) {
      var ColR = colorMixer(col2, col1, 0.01);
      } else if (p < 0.2 && p > 0.0) {
      var ColR = colorMixer(col2, col1, p/0.2);
      } else if (p > 0.2 && p < 0.4) {
      var ColR = colorMixer(col3, col2, (p-0.2)/0.2);
      } else if (p > 0.4 && p < 0.6) {
      var ColR = colorMixer(col4, col3, (p-0.4)/0.2);
      } else if (p > 0.6 && p < 0.8) {
      var ColR = colorMixer(col5, col4, (p-0.6)/0.2);
      } else if (p > 0.8 && p < 1.0) {
      var ColR = colorMixer(col6, col5, (p-0.8)/0.2);
      } else {
      var ColR = colorMixer(col6, col5, 1.0);
      };
      return ColR;
      }
      Zuletzt geändert von MarkusCosi; 27.09.2024, 11:34.

      Kommentar


      • Lenardo
        Lenardo kommentierte
        Kommentar bearbeiten
        kannst du mal einen screenshot davon einstellen bitte

      • MarkusCosi
        MarkusCosi kommentierte
        Kommentar bearbeiten
        Hi Lenardo,

        ich hatte es schon überlegt, will aber hoffentlich verständlicherweise nicht den Grundriss unseres Hauses ins Netz stellen. Ich habe dir eine PM geschickt ..

      • Lenardo
        Lenardo kommentierte
        Kommentar bearbeiten
        Hast du dir den Baustein Anlagenschema auch angesehen, ist zwar nicht farbig aber ähnelt dem was du gemacht hast sehr
    • MarkusCosi
      LoxBus Spammer
      • 28.09.2023
      • 321

      #5
      Ja, siehe Post #1. Den Schema-Baustein nutze ich gern. Aber da hat man halt nur die Zahlen… das wird bei vielen Werten, wie ich finde, recht unübersichtlich… ein schnelles Bild bekommt man so eben grad nicht vermittelt, e.g. wo im Haus ist es besonders warm, wo abweichend warm?

      Kommentar

      • Lightpicture
        Lebende Foren Legende
        • 16.11.2015
        • 3884

        #6
        Guten Morgen MarkusCosi

        Der Statusbaustein bietet Symbole die du in vielen Farben darstellen kannst, je Raum.
        So stelle ich die Temperatur dar.
        FG
        Lightpicture

        Nur ein Netzwerkkabel ist richtiges WLAN

        Kommentar

        • MarkusCosi
          LoxBus Spammer
          • 28.09.2023
          • 321

          #7
          nachdem mir Christian Fenzl freundlicherweise geholfen hat die Temperaturabfrage sinnvoll per .php-Skript zu gestalten (hier), anbei der aktuelle Code für eine .html-Seite die die Temperaturverteilung im Haus anzeigt.

          Die uuids kann man übrigens auch komfortabel über die DNS-webinterface herausbekommen, da die uuids dort bei den entsprechenden Anzeigen in der url mit drin stehen.

          Code:

          HTML-Code:
          <!DOCTYPE html>
          <html>
          <head> <title>Temperaturverteilung</title>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <style>
          
          body {  background-color: #222;
          color: white;
          font-family: Arial, sans-serif;
          text-align: center;  }
          
          .image-container {  background-image: url("http://<<IPLoxberry>>/legacy/TempDistribution/Plan.jpg");
          background-size: contain;
          background-repeat: no-repeat;
          background-position: top left;
          position: relative;
          width: 100%;
          height: 0;
          padding-bottom: 100%;  }  
          </style>
          </head>
          <body>
          <main>
          <div class="image-container">
          <div id="rect_EGWZ" style="width:35%; height:15.1%; position: absolute; top:4.3%; left: 6.25%;"></div>
          ...
          PUT AS MANY ROOMS & RECTANGLES AS NEEDED. Per room you can use several rectangles...
          <div id="ColorScale0" style="width:5%; background-color:rgba(0, 0, 110, 0.4); height:1%; position: absolute; top: 32%; left: 47%;"></div>
          <div id="ColorScale1" style="width:5%; background-color:rgba(54, 79, 201, 0.4); height:2%; position: absolute; top: 30%; left: 47%;"></div>
          <div id="ColorScale2" style="width:5%; background-color:rgba(106, 171, 212, 0.4); height:2%; position: absolute; top: 28%; left: 47%;"></div>
          <div id="ColorScale3" style="width:5%; background-color:rgba(197, 223, 240, 0.4); height:2%; position: absolute; top: 26%; left: 47%;"></div>
          <div id="ColorScale4" style="width:5%; background-color:rgba(237, 210, 209, 0.4); height:2%; position: absolute; top: 24%; left: 47%;"></div>
          <div id="ColorScale5" style="width:5%; background-color:rgba(222, 167, 138, 0.4); height:2%; position: absolute; top: 22%; left: 47%;"></div>
          <div id="ColorScale6" style="width:5%; background-color:rgba(122, 43, 39, 0.4); height:2%; position: absolute; top: 20%; left: 47%;"></div>
          <div id="ColorScale7" style="width:5%; background-color:rgba(200, 0, 0, 0.4); height:1%; position: absolute; top: 19%; left: 47%;"></div>
          </div>
          </main>
          <script>
          //Loxone uuid's for Temperature values:
          //==========Räume==========
          const temperatureURL_EGWZ = 'XX-XXXX-XXXX-XXXXXXXXX'; //uuids of each room's temp
          ...
          PUT AS MANY temps as rooms
          
          const TComf = 23;
          const ScaleA = 1.5;
          const ScaleE = 2;
          
          // Encode the username and password for basic authentication, set user in Loxone Config & authorize on Temp data
          const authString = btoa('LoxberryUser:LoxBerryPW');
          
          function fetchData(url, container, rect) {
          fetch('http://<<IPLoxberry>>/admin/system/tools/filemanager/system/webfrontend/html/XL/user/getvalue.php?name=' + url, {  headers: {
          'Authorization': 'Basic ' + authString
          } })
          .then((response) => response.text())
          .then((text) => { // Extract the desired value from the JSON response and update the data on the screen
          text = text.substring(text.indexOf("val:") + 5);
          text = text.substring(0, text.indexOf("°"));
          const value = text;
          const currentValue = parseFloat(container.textContent);
          if (currentValue !== value) {  container.textContent = value;
          container.style.fontSize = "50%"; // "x-small";
          container.style.color = "white";
          container.style.textAlign = "center";
          container.style.fontStyle = "italic";
          container.style.fontWeight = "normal";
          container.style.overflow = "visible";
          container.style.verticalAlign = "middle";
          container.style.lineHeight = "200%";
          for (let i = 0; i < rect.length; i++) {  document.getElementById(rect[i]).style.backgroundColor=colorScheme(ScaleF(value)) ;
          document.getElementById(rect[i]).style.opacity=0.4;  }
          container.style.animation = 'pulse 1s';
          setTimeout(() => {  container.style.animation = '';  }, 1000);  } })
          .catch(error => console.error('Error fetching data:', error));
          }
          
          //Add Color Scale
          function ScaleF(T) { var ScaleFactor = 0.5 + ScaleA*(T-TComf)/TComf;
          return ScaleFactor; }
          function TfromV(v) { var Temp = (v-0.5)*TComf/ScaleA + TComf;
          return Temp; }
          for (let i = 1; i <= 6; i++) { document.getElementById('ColorScale' + i).textContent = (+TfromV((2*i-1)/12)).toFixed(1) + " °C";
          document.getElementById('ColorScale' + i).style.fontSize = "xx-small";
          document.getElementById('ColorScale' + i).style.textAlign = "center"; }
          document.getElementById('ColorScale0').textContent = (+TfromV(-ScaleE)).toFixed(1) + " °C";
          document.getElementById('ColorScale0').style.fontS ize = "xx-small";
          document.getElementById('ColorScale0').style.textA lign = "center";
          document.getElementById('ColorScale7').textContent = (+TfromV(ScaleE)).toFixed(1) + " °C";
          document.getElementById('ColorScale7').style.fontS ize = "xx-small";
          document.getElementById('ColorScale7').style.textA lign = "center";
          
          //fetch Data function
          function FetchData() { fetchData(temperatureURL_EGWZ, document.getElementById('rect_EGWZ1'), ['rect_EGWZ1','rect_EGWZ2', 'rect_EGWZ3a', 'rect_EGWZ3b', 'rect_EGWZ3c']);
          ...← example of a room modeled by several rectangles rect_EGWZ1 ...
          PUT AS MANY temps as rooms }
          
          // Fetch the initial data when the page loads
          FetchData();
          
          // Schedule updates every 120 seconds = 2 minutes
          setInterval(() => { FetchData(); }, 120000);
          
          //color functions for java
          dec2hex = function (d) { if (d > 15)
          { return d.toString(16) } else
          { return "0" + d.toString(16) } }
          rgb = function (r, g, b) { return "#" + dec2hex(r) + dec2hex(g) + dec2hex(b) };
          
          //colorChannelA and colorChannelB are ints ranging from 0 to 255
          function colorChannelMixer(colorChannelA, colorChannelB, amountToMix){ var channelA = colorChannelA*(1-amountToMix);
          var channelB = colorChannelB*amountToMix;
          return parseInt(channelA+channelB); }
          //rgbA and rgbB are arrays, amountToMix ranges from 0.0 to 1.0
          //example (red): rgbA = [255,0,0]
          function colorMixer(rgbA, rgbB, amountToMix){ var r = colorChannelMixer(rgbA[0],rgbB[0],amountToMix);
          var g = colorChannelMixer(rgbA[1],rgbB[1],amountToMix);
          var b = colorChannelMixer(rgbA[2],rgbB[2],amountToMix);
          return "rgb("+r+","+g+","+b+")"; }
          //color scheme as a function
          function colorScheme(p){ const col0 = [0, 0, 110]; //darkest blue
          const col1 = [54, 79, 201]; //dark blue
          const col2 = [106, 171, 212]; //darker blue
          const col3 = [197, 223, 240]; //light blue
          const col4 = [237, 210, 209]; //light red
          const col5 = [222, 167, 138]; //light orange
          const col6 = [122, 43, 39]; //dark red
          const col7 = [200, 0, 0]; //darkest red
          if (p <= -3.0) {  var ColR = colorMixer(col0, col1, 0);  } else if (p > -3.0 && p <= 0.0) {  var ColR = colorMixer(col0, col1, p/ScaleE + 1.0);  } else if (p > 0 && p <= 0.2) {  var ColR = colorMixer(col1, col2, p/0.2);  } else if (p > 0.2 && p <= 0.4) {  var ColR = colorMixer(col2, col3, (p-0.2)/0.2);  } else if (p > 0.4 && p <= 0.6) {  var ColR = colorMixer(col3, col4, (p-0.4)/0.2);  } else if (p > 0.6 && p <= 0.8) {  var ColR = colorMixer(col4, col5, (p-0.6)/0.2);  } else if (p > 0.8 && p <= 1.0) {  var ColR = colorMixer(col5, col6, (p-0.8)/0.2);  } else if (p > 1.0 && p <= 3.0) {  var ColR = colorMixer(col6, col7, (p-1.0)/(ScaleE-1.0));  } else {  var ColR = colorMixer(col6, col7, 1.0);  };  return ColR; }
          </script>
          </body>
          </html>
          Zuletzt geändert von MarkusCosi; 07.10.2024, 10:24.

          Kommentar


          • MarkusCosi
            MarkusCosi kommentierte
            Kommentar bearbeiten
            ps: nach dem Update auf den Loxberry 3.0.1.3 muss der code angepasst werden im fetchData Teil:
            text = text.substring(text.indexOf("name:") + 10);
            text = text.substring(0, text.indexOf("u00b0")-1);
        Lädt...