Ideas and Discussion: What Features Do You Want in openHAB 5.0?

This isn‘t really an openHAB issue but an OS issue. How should openHAB get the meter identification before the thing is ONLINE and linked Items get values?

Have you ever looked at the timeseries for forecasts ? The OWM Binding supports those, therefore you can reduce your number of items and just parse the state of a forecast timeseries Item.

I have at the moment only one on my wishlist
If possible, edit the value of an item.
Maybe with the possibility to give a default value when you create a item

The question is can the timeline picker implemented into openhab as a standard tool?

2 Likes

Hm, in general yes … but there are non-optional configs (is Astro-Binding installed and exist channel for tlp, how to select the right item[s] to switch). How can one check this and handle in case of missing?
At the moment, you have additional to install a rule that is called periodically and switches. This functionality must implemented in the core!? I’m not familiar with java.
Perhaps there are an other option to install multiple files in OH conf folder?? This could reduce the effort to install and use the tlp significant. However, the best approach would be to integrate it into one of the user interfaces.

When adding a new thing, (especially if it is the first thing of a new binding you are adding) I find it useful if MainUI displays the value of each channel so that you can decide whether or not to link an item to that channel.
Especially when your things and items are managed by MainUI this would help a lot in setting up new devices.
Illustration:

7 Likes

Yes - I’m aware. But that’s an issue sml2mqtt solved for me.
Now I’m back to only one meter and still use it because the pre processing of values is a huge benefit for me.


Out of curiosity - do you have by any change an example how I would find the maximum temperature of the daily forecast that way?
When time series were added I looked into supporting them in HABApp but the API was very limited so I currently am ignoring the timeseries events completely. :person_shrugging:

Edit: if you don’t have the example handy don’t waste time on it :slight_smile:

Sorry, I don’t have an example, but might find some time to dig into it.

Depending on the type of the Item and sometimes the semantic tags, the default widget almost always allows for commanding the Item with no additional configuration.

It should also be commandable from the developer sidebar.

image

And of course, if it is semantically tagged it can be commanded from the Overview page.

Some additional ways to manually update or command an Item include the karaf console, API Explorer, or the -Scratchpad` Script.

I don’t use it myself (yet… so many things to do, so little time…), but

MyItem.maximumBetween(starttimestamp,endtimestamp)

I don’t think that’s a great fit for this, but I’m going to explore it for some other applications. Thanks for drawing my attention to it.

I would like to see a enhancement in sitemap for displaying Grafana views. I wrote with my knowledge the code below to make it possible to move back in time in the same window as selected. Something like this would be perfect. Ignore the test under the IFrames.
image

<!DOCTYPE html>
<html lang="nl">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Tijdweergave en COP Warmtepomp</title>
  <!--  <link rel="stylesheet" href="css/index.css"> -->
  <style>
    :root {
      --buttonBackgroudColor: #d9d9d9;
      --buttonBackgroudSelectedColor: #4CAF50;
      --buttonTextSelectedColor: white;
      --buttonTextColor: black;
      --buttonTextNotSelectableColor: lightGray;
      --buttonBoxColor: rgb(59, 25, 151);
      --buttonBorderColor: rgb(71, 62, 94);
      --borderColor: #ddd;
    }

    body,
    html {
      font-family: Arial, sans-serif;
      width: 100vw;
      height: 100vh;
      overflow-y: auto;
    }

    .container {
      display: flex;
      flex-direction: column;
      align-items: center;
      width: calc(100vw - 20px);
      height: calc(100vh - 20px);
      padding: 10px;
      box-sizing: border-box;
    }

    section {
      display: flex;
      flex-direction: column;
      align-items: center;
      width: 100%;
    }

    /* Control buttons and time selector */
    .control-buttons {
      display: flex;
      gap: 15px;
      justify-content: center;
      margin-bottom: 10px;
    }

    .control-buttons #minBtn,
    .control-buttons #plusBtn {
      padding: 6px 12px;
      font-size: 0.8rem;
      border-radius: 4px;
      cursor: pointer;
      border: none;
      background-color: var(--buttonBackgroudColor);
    }

    .time-selector {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      justify-content: center;
      margin-bottom: 10px;
    }

    .time-selector label {
      cursor: pointer;
      padding: 6px 12px;
      font-size: 14px;
      border-radius: 4px;
      background-color: var(--buttonBackgroudColor);
      color: var(--buttonTextColor);
    }

    .time-selector input[type="radio"] {
      display: none;
    }

    .time-selector input[type="radio"]:checked+label {
      background-color: var(--buttonBackgroudSelectedColor);
      color: var(--buttonTextSelectedColor);
    }

    /* Grid for iframes */
    .grid-container {
      display: grid;
      gap: 15px;
      width: 100%;
      height: 100%;
      grid-template-columns: 1fr;
    }

    .grid-item iframe {
      width: 100%;
      height: 100%;
      border: 1px solid var(--borderColor);
      border-radius: 4px;
    }

    /* Layout for very small screens */
    @media (max-width: 375px) {
      .control-buttons {
        gap: 3px;
        margin-bottom: 10px;
      }

      .time-selector {
        flex-direction: row;
        gap: 3px;
      }

      .time-selector label {
        cursor: pointer;
        padding: 6px 10px;
        font-size: 1rem;
      }
    }

    /* Responsive styling */
    @media (min-width: 768px) {

      /* Control-buttons and time-selector centered on one row */
      .button-container {
        display: flex;
        flex-direction: row;
        justify-content: center;
        align-items: center;
        width: 100%;
        gap: 15px;
      }

      .grid-container {
        grid-template-columns: 1fr 1fr;
      }
    }

    @media (min-width: 1024px) {
      .grid-container {
        grid-template-columns: 1fr 1fr 1fr;
      }
    }
  </style>
  <!--  <script src="script.js"></script> -->
  <script>
    document.addEventListener('DOMContentLoaded', function () {
      const iframeIds = ["WarmtepompIframe", "VandaagIframe", "OmvormersIframe", "OmvormersSpanningIframe", "WeerIframe"];
      const minBtn = document.getElementById('minBtn');
      const plusBtn = document.getElementById('plusBtn');
      const timeSelector = document.getElementById('timeSelector');

      const timeSettings = {
        "now-1h": { unit: "h", display: "1u" },
        "now-4h": { unit: "h", display: "4u" },
        "now-12h": { unit: "h", display: "12u" },
        "now-24h": { unit: "h", display: "24u" },
        "now-1w": { unit: "w", display: "1W" },
        "now-1M": { unit: "M", display: "1M" },
        "now-1y": { unit: "y", display: "1J" }
      };

      let offset = 0;
      let currentTimeRange = "now-1h";

      // Functies voor het dynamisch instellen van tijd, iframes, en knoppen
      function generateTimeOptions() {
        Object.keys(timeSettings).forEach((key, index) => {
          const input = document.createElement('input');
          input.type = 'radio';
          input.id = `option${index}`;
          input.name = 'time';
          input.value = key;
          if (key === currentTimeRange) input.checked = true;

          const label = document.createElement('label');
          label.setAttribute('for', `option${index}`);
          label.innerText = timeSettings[key].display;

          timeSelector.appendChild(input);
          timeSelector.appendChild(label);

          input.addEventListener('change', function () {
            currentTimeRange = input.value;
            offset = 0;
            updateButtons();
            iframeIds.forEach(updateIframe);
          });
        });
      }

      function updateIframe(iframeId) {
        const iframe = document.getElementById(iframeId);
        let currentSrc = iframe.src;
        const { unit } = timeSettings[currentTimeRange];
        const intervalValue = parseInt(currentTimeRange.slice(4));
        const newFrom = `now-${(offset + 1) * intervalValue}${unit}`;
        const newTo = `now-${offset * intervalValue}${unit}`;
        currentSrc = currentSrc
          .replace(/from=[^&]*/, `from=${newFrom}`)
          .replace(/to=[^&]*/, `to=${newTo}`);
        iframe.src = currentSrc;
      }

      function updateButtons() {
        const { display } = timeSettings[currentTimeRange];
        minBtn.innerText = `- ${display}`;
        plusBtn.innerText = `+ ${display}`;
        plusBtn.style.color = offset === 0 ? 'var(--buttonTextNotSelectableColor)' : 'var(--buttonTextColor)'
        plusBtn.disabled = offset === 0;
      }

      function adjustIframeHeights() {
        let ratio;
        if (window.innerWidth <= 375) {
          ratio = 0.8;
        } else if (window.innerWidth <= 768) {
          ratio = 0.6;
        } else if (window.innerWidth <= 1024) {
          ratio = 0.7;
        } else {
          ratio = 0.4;
        }

        iframeIds.forEach((iframeId) => {
          const iframe = document.getElementById(iframeId);
          if (iframe) {
            const width = iframe.clientWidth;
            iframe.style.height = (width * ratio) + 'px';
          }
        });
      }

      // Eventlisteners voor de knoppen
      minBtn.addEventListener('click', function () {
        offset++;
        updateButtons();
        iframeIds.forEach(updateIframe);
      });

      plusBtn.addEventListener('click', function () {
        if (offset > 0) offset--;
        updateButtons();
        iframeIds.forEach(updateIframe);
      });

      // Tijdweergave functie
      function updateTime() {
        const now = new Date();
        const hours = now.getHours().toString().padStart(2, '0');
        const minutes = now.getMinutes().toString().padStart(2, '0');
        const seconds = now.getSeconds().toString().padStart(2, '0');
        document.getElementById('time').innerText = `${hours}:${minutes}:${seconds}`;
      }

      // Initialisaties bij het laden van de pagina
      generateTimeOptions();
      updateButtons();
      iframeIds.forEach(updateIframe);
      adjustIframeHeights();
      updateTime();

      // Eventlisteners voor dynamische aanpassingen bij schermverandering
      window.addEventListener('resize', adjustIframeHeights);

      setInterval(updateTime, 1000); // Tijd update elke seconde
    });

    // Functie om de COP waarde op te halen van OpenHAB
    function fetchCOPValue() {
      fetch('http://192.168.100.50:8080/rest/items/COPWarmtepomp/state')
        /*  fetch('/rest/items/COPWarmtepomp/state')*/

        .then(response => response.text())
        .then(data => {
          document.getElementById('copValue').innerText = `${parseFloat(data).toFixed(3)}`;
        })
        .catch(error => {
          console.error('Error fetching COP value:', error);
          document.getElementById('copValue').innerText = 'Error';
        });
    }

    // Haal de waarde elke 10 seconden op
    setInterval(fetchCOPValue, 10000);
    fetchCOPValue();


    // Functie om de geselecteerde tijd om te zetten naar cron-formaat en naar OpenHAB te sturen
    function sendCron() {
      const timePicker = document.getElementById('timePicker').value;
      if (!timePicker) {
        alert("Selecteer een tijd voordat je deze verstuurt!");
        return;
      }
      const [hours, minutes] = timePicker.split(":");

      // Cron-formaat: 0 MM HH * * ?
      const cronExpression = `0 ${minutes} ${hours} * * ?`;
      console.log("Cron expression:", cronExpression);

      // Stuur de cron-uitdrukking naar OpenHAB (vervang OpenHAB-item met jouw item)
      fetch('http://192.168.100.50:8080/rest/items/WarmtepompAvondTijd', {
        method: 'POST',
        headers: {
          'Content-Type': 'text/plain'
        },
        body: cronExpression
      })
        .then(response => {
          if (response.ok) {
            alert("Cron tijd succesvol verstuurd naar OpenHAB!");
          } else {
            alert("Er ging iets mis met het versturen van de tijd naar OpenHAB.");
          }
        })
        .catch(error => {
          console.error('Error sending cron to OpenHAB:', error);
          alert('Error bij het versturen van de tijd naar OpenHAB.');
        });
    }


  </script>
</head>

<body>

  <div class="control-buttons">
    <button id="minBtn"></button>
    <button id="plusBtn"></button>
  </div>

  <div class="time-selector" id="timeSelector"></div>
  <div class="container">
    <section>
      <div class="grid-container">
        <div class="grid-item">
          <iframe id="WarmtepompIframe"
            src="http://192.168.100.92:3000/d-solo/b1e3d190-620d-4b9a-9aba-4fe921aa8860/warmtepomp?orgId=1&panelId=1&from=${from}&to=${to}"
            frameborder="0">
          </iframe>
        </div>
        <div class="grid-item">
          <iframe id="VandaagIframe"
            src="http://192.168.100.92:3000/d-solo/ec543d7f-787a-4eb0-ae2b-7651285c96a3/new-dashboard?orgId=1&from=${from}&to=${to}&panelId=1"
            frameborder="0">
          </iframe>
        </div>
        <div class="grid-item">
          <iframe id="OmvormersIframe"
            src="http://192.168.100.92:3000/d-solo/dfbb21ae-feaa-4ae7-ac01-b352fdce08c2/omvormers-actueel?orgId=1&from=${from}&to=${to}&panelId=1"
            frameborder="0">
          </iframe>
        </div>
        <div class="grid-item">
          <iframe id="OmvormersSpanningIframe"
            src="http://192.168.100.92:3000/d-solo/fb8c184b-c0c3-48b8-bc12-7c55af8e0840/omvormer-netwerkspanning?orgId=1&panelId=1&from=${from}&to=${to}"
            frameborder="0">
          </iframe>
        </div>
        <div class="grid-item">
          <iframe id="WeerIframe"
            src="http://192.168.100.92:3000/d-solo/d73ec2e4-b54b-45a6-b676-7e651071a6b2/weer?orgId=1&from=${from}&to=${to}&panelId=1"
            frameborder="0">
          </iframe>
        </div>
      </div>
    </section>

    <h2>De huidige tijd is:</h2>
    <div id="time"></div>

    <div class="container">
      <h2>COP Warmtepomp waarde zo kan het ook:</h2>
      <div id="copValue">Laden...</div>
    </div>

    <div class="container">
      <h2>Selecteer een tijd:</h2>
      <input type="time" id="timePicker">
      <button onclick="sendCron()">Stuur tijd naar OpenHAB</button>
    </div>
</body>

</html>
1 Like

I hope that the current way of using DSL will stay.

3 Likes

I would love to have this.

1 Like

Given OH has native charting and given this way of interacting with Grafana is essentially dictated by their API, I’m not sure it makes a whole lot of sense to implement as part of the OH UIs natively. Though it sounds like a great idea for a custom marketplace widget or as an add-on or the like.

In a lot of ways, keeping Rules DSL as part of core has caused problems and slowed down development in the past and is likely to continue to into the future. It is a tiny imposition on end users to have them deliberately choose to install Rules DSL as an add-on instead of it comming by default. And I’m sure if you upgraded from OH 4- to OH 5 the add-on would be installed automatically as part of the upgrade process.

But I also think it is unlikely to happen in OH 5.

I had forgotten I had to jump through hoops to get it set up, but I think the semanticHomeMenu is an invaluable part of my openHAB setup. Especially for the lady of the house. (:pray:t2: @hmerk)

I would not be able to create that myself, my page and widget magic is very limited. But even setting it up was a bit of a quest.

I think having such an overview out of the box would be a very nice feature.

Maybe the option of accessing the console in the browser is also not inconvenient. It would allow for debugging when not at home. But maybe that’s technically impossible/undesirable…

I am already evaluating how we could achieve this…

2 Likes

First of all “Thank you!” for all the developers of OH. It is a great tool!

Still, I think there is room for improvements:

  • Templates which cover item generation, rules and GUI in one go
  • Extension of the REST API to include GUI (maybe it exists but I have not been able to use it)
  • Handling of arrays of items (e.g. an array of DateTime items with on/off times for a switch)

Here an example to clarify what I mean:
Integration of a smart plug to switch on and off at a certain time.
Steps needed:

  1. Create thing “SmartPlug”
  2. Create item “SmartPlugItem”
  3. Create two DateTime items to switch on and off “SmartPlugItem”
  4. Create rule which is triggered by DateTime items
  5. Create GUI to set DateTime items
  6. Repeat steps 3-5 if more than one on/off time is needed

This is a lot of effort for a rather simple task.
I handle steps 2-4 with python scripts and the REST API. Step 5 does not work for me in this way because of the missing interface in the API.

Why not just:

  1. Create thing “SmartPlug”
  2. Create item “SmartPlugItem”
  3. Create rule which is triggered by Time event

?

Dear Erik,
maybe I am wrong but for the “Time event” trigger in the rule you need a DateTime item you have to create first.
So for the task you need: 1 thing, 3 items (one for the switch, 2 for the times), 2 rules (maybe one if you toggle or make it a bit more clever), 2 GUI elements to set the on/off times (if they should be user controlled). For me this seems quite a lot for a rather simple task and I think it would be great to have a mechanism to automatize the steps.

1 Like