Zeigt her eure Dashboards!

und zusätzlich:

QHOME Energie ist mein persönliches Dashboard für Home Assistant zur Visualisierung von PV, Batterie und Energieflüssen. Statt möglichst viele Sensoren darzustellen, liegt der Fokus auf einer modernen, appähnlichen Oberfläche und einer klaren Zusammenfassung des aktuellen Energiezustands. Das Projekt ist aktuell noch individuell auf meine Home-Assistant-Installation zugeschnitten.





woe kommst du an die Pooldaten ?

Die Wasserwerte (pH, freies Chlor, CYA usw.) stammen aus dem PoolLab 2.0. ( HACS ) und werden nach jeder Messung in Home Assistant übernommen. Die Pooltemperatur kommt von der Inverter Wärmepumpe ( Local Tuya / HACS ). Die Gemke-Anlage habe ich über einen Shelly und eigene Automationen integriert – Laufzeiten und dosierte Chlormengen werden daraus berechnet. Poolpumpe ( Restlaufzeit ) läuft auch über Shelly ( oder Tasmota :slight_smile: )

(Beitrag vom Verfasser gelöscht)

@anon406544 mich würde interessieren, wie Du die Schrift hingekriegt hast, die sieht nämlich gut aus.

Das ist ein tolles Dashboard. Sehr übersichtlich und eine sehr schöne Navigation.
Hierzu habe ich eine Frage/Bitte: Wie hast du die Navigation unten im Dashboard festgetakkert? Kannst du mir bitte eine (bis auf die Navigationselemente) leere Seite schicken (gerne per PN) oder hier posten?
Ich komme mit meiner Navigation aktuell nicht weiter. Ich habe bei mir mit Batches gemacht, die sind aber oben in der Titelleiste.

Du musst die App auf dem Handy installieren die macht das ganze um die Navigation. Im browser geöffnet verhält sich alles anders.

Das ist die Lovelace Navbar-card. Installieren, in die Seite einfügen und dann gibt es viele Einstellmöglichkeiten

Das ist meine Config:

type: custom:navbar-card
mobile:
  show_labels: true
  mode: docked
desktop:
  show_labels: true
  position: bottom
routes:
  - url: /pool-dashboard/pool
    icon: mdi:home-outline
    icon_selected: mdi:home
    label: Home
  - url: /pool-dashboard/pool-wasser
    icon: mdi:water-outline
    icon_selected: mdi:water
    label: Wasser
  - url: /pool-dashboard/pool-technik
    icon: mdi:cog-outline
    icon_selected: mdi:cog
    label: Technik
  - url: /pool-dashboard/pool-verlauf
    icon: mdi:chart-line
    icon_selected: mdi:chart-line
    label: Verlauf
styles: |
  .navbar {
    --navbar-primary-color: #2f8fe0;
    --navbar-background-color: rgba(9,9,10,.94);
    --navbar-route-icon-size: 23px;
  }
  .navbar-card {
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border-top: 1px solid rgba(255,255,255,.07);
    box-shadow: 0 -8px 30px rgba(0,0,0,.35) !important;
  }
  .icon, .label { color: rgba(220,220,220,.52); }
  .icon.active, .label.active { color: #2f8fe0; }

und dann noch Kiosk Mode um die obere Navigation auszublenden:

Hi, @anon406544 , in meiner Konstellation verhalten sich Browser und App gleich.

Die Installation hat funktioniert. Ich habe die Karte nun installiert, ich kannte sie nicht.
Das sieht schon mal prima aus, eine Frage habe ich dazu:
Kann man die Navigation auch unten linksbündig einbinden?

Funktioniert mit “mode:floating” und styles, allerdings müsste dann die Seitenleiste ausgeblendet werden.

type: custom:navbar-card
desktop:
  position: bottom
  mode: floating
styles: |
  .navbar.desktop.bottom {
    left: 16px !important;
    right: auto !important;
    transform: none !important;
  }
routes:
  ...

ansonsten mit den Werten spielen bis es einigermaßen passt. Ist m.E. ein Workaround da px. Positionierung.

styles: |
  .navbar.desktop.bottom {
    left: 210px !important;
    right: auto !important;
    transform: none !important;
  }

Hast du da eine Config für? Das sieht sehr gut aus

Danke dir, freut mich! Eine fertige Config zum Teilen habe ich aktuell leider nicht - das Dashboard ist mit custom:button-card gebaut und fast jede Karte hat eigenen, handgeschriebenen JS-/Jinja-Code, der direkt auf meine spezifischen Entity-IDs (Fronius, Speicher, PV-Prognose etc.) zugeschnitten ist. Das ist super flexibel für mich, da die Cards sehr flexible entwickelt werden können, aber eben nicht generisch - eine 1:1-Weitergabe würde bei jedem anderen System einfach nicht laufen, ohne dass man quasi jede Karte einzeln an die eigenen Entities anpasst. Um das wirklich teilbar zu machen, müsste ich es erst als Vorlage mit Variablen/Platzhaltern abstrahieren, das ist mein Plan um es als Community-Version zu entwickeln, aber aktuell leider noch nicht.

Vielen Dank, mit dem unteren Code hat es geklappt. Ich habe den Wert Links angepasst.
Gruß
Martin

Entschuldige die sehr späte Antworte. Ich gucke hier nur sporadisch rein.
Inzwischen habe ich mein Dashboard ganz anders und für Tablet und Phone umstrukturiert, sodass ich nur noch ein einziges nutze.

Trotzdem will ich versuchen, dir zu helfen. Ich habe damals Unteransichten erstellt und mit einem Dummytaster (inzwischen gibt es eine Verknüpfungskarte) auf die Unterseite verlinkt. Das Problematische dabei war, wenn man die Struktur hat ändern wollen, musste man das manuell auch allen Unterseiten gleichermaßen wiederholen in der Anpassung.

Jetzt nutze ich die Navbar-HACS-Integration, mit der ich auch Badges anzeigen kann, wenn Meldungen vorliegen. Eigentlich sehr ähnlich zu iOS. Unterseiten verwende ich aber weiterhin. Und ich habe mich dazu durchgerungen, weniger auf das Dashboard zu legen, weil das sonst auch schnell unübersichtlich wird. Mit einem schicken Hintergrundbild sieht das gleich nochmal professioneller aus.

Nie fertig Werden… Das kennt man. :laughing:

Hallo zusammen,
ich möchte euch mein aktuelles “QHOME”-Wandpanel für Home Assistant zeigen. Es ist für ein iPad im Querformat optimiert und bündelt Hausstatus, Energieflüsse, Klima, Pool, Kameras und wichtige Schnellfunktionen in einer ruhigen Oberfläche. Viele Details und Bedienelemente öffnen sich bei Bedarf als Pop-up, damit die Übersicht kompakt bleibt.

Das Projekt ist aktuell noch individuell auf meine Home-Assistant-Installation, Entitäten und Abläufe zugeschnitten – also noch keine direkt übertragbare Standardlösung.

Coole Sache würdest du die yaml teilen?

Mega,

ich wäre sehr an dem Code für das Dashboard interessiert.

Sieht echt geil aus. :heart_eyes:

Danke euch! Das komplette Dashboard ist leider zu sehr mit meinen eigenen Entities, Helpern und Backend-Paketen verzahnt, um es 1:1 zu teilen — würde bei euch so nicht laufen. Es ist was, was auf meinem Plan steht das skalierbarer zu machen, für mich und dann auch für die Community.

Aber hier eine Karte aus dem gleichen Baukasten als eigenständiges Beispiel, komplett unabhängig testbar: eine Klima-Zeilenkarte mit farbcodiertem Status-Icon, aktueller Temperatur und +/- Stepper mit sofortigem UI-Feedback (kein Warten auf den Service-Call).

Voraussetzung: nur die HACS-Karte button-card — keine weiteren Templates nötig.

Setup: einfach eure eigenen climate.*-Entity-IDs in die climate_entities:-Liste eintragen — Name, Icon und Zustandsfarbe kommen automatisch aus dem jeweiligen Entity.

Das Grundmuster (optimistisches UI + debounced Service-Call, Icon-Tap öffnet More-Info) ist das gleiche, das ich für alle Stepper-Controls im Dashboard verwende — lässt sich auf andere Domains (z. B. light mit Helligkeit) übertragen.

Grundsätzlich ist jede Karte im Dashboard einzeln von Hand aufgebaut — vorher steht aber ein festes Design-Konzept (Farben, Typografie, Abstände, Radien), das dann Karte für Karte umgesetzt und live getestet wird, damit am Ende alles wie aus einem Guss wirkt.

# Standalone custom:button-card, keine externen Templates noetig.
# Benoetigt nur HACS "button-card" (https://github.com/custom-cards/button-card).
#
# Setup: einfach eure eigenen climate.*-Entity-IDs in die Liste unten
# (climate_entities) eintragen — Name, Icon und Zustandsfarbe werden
# automatisch aus dem jeweiligen Entity gezogen. Kein Label/Icon pro
# Raum noetig.
#
# Was sie zeigt:
#   - pro Entity: Status-Icon (Farbe + Symbol je nach HVAC-Modus),
#     Name, aktuelle Temperatur, +/- Stepper mit optimistischem
#     UI-Update + debounced set_temperature-Call, Tap aufs Icon
#     oeffnet die native More-Info.
#


type: custom:button-card
show_name: false
show_icon: false
show_state: false
triggers_update: all
tap_action:
  action: none
variables:
  title: Klima
  climate_entities:
    - climate.living_room
    - climate.bedroom
    - climate.office
styles:
  card:
    - background: rgba(20, 20, 22, 0.72)
    - border: 1px solid rgba(255, 255, 255, 0.07)
    - border-radius: 18px
    - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28)
    - padding: 16px
  grid:
    - grid-template-areas: '"content"'
  custom_fields:
    content:
      - pointer-events: auto
      - position: relative
      - z-index: 5
custom_fields:
  content: |
    [[[
      if (!window.__qhomeDummyPending) window.__qhomeDummyPending = {};
      if (!window.__qhomeDummyTimers) window.__qhomeDummyTimers = {};

      const ACCENT = '#4AA8FF';
      const MUTED = 'rgba(190,190,190,0.50)';

      const MODE_PRESENTATION = {
        heat: { icon: 'mdi:fire', color: '#FF9E45' },
        cool: { icon: 'mdi:snowflake', color: ACCENT },
        dry: { icon: 'mdi:water-percent', color: '#56D63F' },
        fan_only: { icon: 'mdi:fan', color: ACCENT },
        auto: { icon: 'mdi:thermostat-auto', color: ACCENT },
        off: { icon: 'mdi:thermostat', color: MUTED }
      };

      function fmt(value) {
        return Number.isFinite(value) ? value.toFixed(1).replace('.', ',') : '–';
      }

      function labelFor(entityId, state) {
        return state?.attributes?.friendly_name || entityId;
      }

      window.__qhomeDummyAdjust = function (entityId, direction, targetId) {
        const st = hass.states[entityId];
        if (!st) return;

        const step = st.attributes.target_temp_step || 0.5;
        const min = st.attributes.min_temp ?? 16;
        const max = st.attributes.max_temp ?? 30;

        const pending = window.__qhomeDummyPending[entityId];
        const base = Number.isFinite(pending) ? pending : Number(st.attributes.temperature);
        const next = Math.min(max, Math.max(min, Math.round((base + direction * step) * 10) / 10));

        window.__qhomeDummyPending[entityId] = next;

        const targetEl = document.getElementById(targetId);
        if (targetEl) targetEl.textContent = fmt(next) + ' °C';

        clearTimeout(window.__qhomeDummyTimers[entityId]);
        window.__qhomeDummyTimers[entityId] = setTimeout(() => {
          hass.callService('climate', 'set_temperature', {
            entity_id: entityId,
            temperature: window.__qhomeDummyPending[entityId]
          }).catch((err) => console.error('QHOME dummy: set_temperature failed', err))
            .finally(() => { delete window.__qhomeDummyPending[entityId]; });
        }, 400);
      };

      window.__qhomeDummyMoreInfo = function (entityId) {
        document.querySelector('home-assistant')?.dispatchEvent(
          new CustomEvent('hass-more-info', { bubbles: true, composed: true, detail: { entityId } })
        );
      };

      const entities = variables.climate_entities || [];

      const rows = entities.map((entityId, index) => {
        const st = hass.states[entityId];
        const unavailable = !st || ['unavailable', 'unknown'].includes(st.state);
        const mode = st ? st.state : 'off';
        const presentation = MODE_PRESENTATION[mode] || MODE_PRESENTATION.off;

        const label = labelFor(entityId, st);
        const current = st ? Number(st.attributes.current_temperature) : NaN;
        const pending = window.__qhomeDummyPending[entityId];
        const target = Number.isFinite(pending) ? pending : (st ? Number(st.attributes.temperature) : NaN);
        const targetId = 'qhome_dummy_target_' + entityId.replace(/\./g, '_');
        const isLast = index === entities.length - 1;

        return `
          <div style="
            display:flex;align-items:center;gap:12px;padding:12px 2px;
            ${isLast ? '' : 'border-bottom:1px solid rgba(255,255,255,0.055);'}
          ">
            <ha-icon icon="${presentation.icon}"
              style="--mdc-icon-size:22px;color:${unavailable ? MUTED : presentation.color};flex:0 0 auto;cursor:pointer;"
              onclick="window.__qhomeDummyMoreInfo('${entityId}')"
            ></ha-icon>
            <div style="flex:1;min-width:0;">
              <div style="font-size:14px;color:#F4F4F4;">${label}</div>
              <div style="font-size:12px;color:rgba(220,220,220,0.72);margin-top:2px;">
                ${unavailable ? 'Nicht verfügbar' : fmt(current) + ' °C aktuell'}
              </div>
            </div>
            <button
              style="width:30px;height:30px;border-radius:9px;border:none;background:rgba(24,24,27,0.82);color:#F4F4F4;font-size:17px;line-height:1;"
              ${unavailable ? 'disabled' : ''}
              onclick="event.stopPropagation(); window.__qhomeDummyAdjust('${entityId}', -1, '${targetId}');"
            >–</button>
            <div id="${targetId}" style="min-width:62px;text-align:center;font-size:14px;font-weight:600;color:${unavailable ? MUTED : ACCENT};">
              ${fmt(target)} °C
            </div>
            <button
              style="width:30px;height:30px;border-radius:9px;border:none;background:rgba(24,24,27,0.82);color:#F4F4F4;font-size:17px;line-height:1;"
              ${unavailable ? 'disabled' : ''}
              onclick="event.stopPropagation(); window.__qhomeDummyAdjust('${entityId}', 1, '${targetId}');"
            >+</button>
          </div>
        `;
      }).join('');

      return `
        <div>
          <div style="text-align:center;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:rgba(220,220,220,0.72);margin-bottom:8px;">
            ${variables.title}
          </div>
          ${rows}
        </div>
      `;
    ]]]