Show off your favourite dashboard creations!

I have been playing with card-mod and came up with this - this screenshot is one of the dashboards I cast to my Nest Hub.

I hope this inspires others. I couldn’t find the sort of design I wanted, so rolled up my sleeves and had a play!

As an aside - the screenshot is from the desktop - the buttons etc. are all correct on the cast version. Curious!!!

Love your dashboards. What theme are you using? Any chance that you could post it if it is your won theme?

Hi and thanks! I posted my theme’s YAML a few replies above.

I’ve recently updated it (merged dark and light into one single file) and just tried posting the updated version here. Unfortunately, it doesn’t fit in the chat window. Maybe someday I’ll find the motivation to upload the whole setup to github.

1 Like

It was actually more the cards I was after - I ended up vibe coding my own custom card today that grabs the relevant entities from the parent area. works pretty well I think (new cards are at the bottom of the page) still a little work to go.

Proudly introducing my work :wink:

Normal mode:

Cleaning mode:

2 Likes

With Claude’s help I created this dashboard that I’m running on a FireHD tablet mounted on my refrigerator. It combines the really cool Split-Flap display card by RazManSource that shows AI-generated headlines (with some snarkiness for added flavor), stock news, and weather with a rotating 3-column news feed showing real RSS headlines with photos. You can tap any story and it opens in the browser and swipe left to go back to the dashboard.

If anyone’s interested I have a full write-up with code.

1 Like

Yes please!

Roger that. Here’s the full writeup! You should definitely watch your AI token usage if you take this path.

Kitchen News Board — A Splitflap + RSS Dashboard for Home Assistant

I wanted a kitchen dashboard that felt more like ambient information and less like staring at my phone. After some experimentation, I landed on a two-part layout: a retro split-flap board at the top showing AI-generated headlines, stock news, and weather — and a rotating 3-column news feed below showing real RSS headlines with photos. Tap any story and it opens in your browser. Swipe left to get back.

Here’s how it works.


The Layout

The dashboard uses custom:grid-layout (from the Layout Card HACS plugin) with two rows:

  • Top rowcustom:splitflap-card, full width, auto height
  • Bottom row — a custom news-headlines-card web component, takes the remaining height
views:
  - title: News Board
    type: custom:grid-layout
    layout:
      grid-template-columns: 1fr
      grid-template-rows: auto 1fr
      margin: "0"
      padding: "0"
    cards:
      - type: custom:splitflap-card
        entity: input_text.splitflap_message
        rows: 5
        columns: 36
        font_size: 48px
        line_separator: "|"
        word_wrap: false
        scramble_duration: 500
        stagger_delay: 15
        sound: false
        accent_color: "#e8572a"
        view_layout:
          grid-column: "1"
          grid-row: "1"

      - type: custom:news-headlines-card
        entities:
          - sensor.npr_news
          - sensor.the_verge
          - sensor.deadline
          - sensor.the_hill
        source_names:
          - NPR News
          - The Verge
          - Deadline
          - The Hill
        rotate_seconds: 60
        view_layout:
          grid-column: "1"
          grid-row: "2"

The dashboard has two additional views (swiped to with Swipe Navigation) for the kitchen media player and lyrics display, but those are separate from the news board itself.


The Splitflap Board

The splitflap card is Split-Flap Display Card by RazManSource, available in HACS. It watches an input_text helper and animates whenever the value changes.

The board rotates between 6 slots: weather, stocks, packages, entertainment, tech, and weather alerts. Each slot is a separate input_text helper. A rotate automation cycles through them every 20-30 seconds.

AI-Generated Content

Three of the slots are filled by Claude Haiku via the Anthropic REST API. Each automation runs on a staggered schedule to avoid hitting the 30,000 tokens/minute rate limit:

  • Weather — every 3 hours at :00 (uses local sensor data, no web search)
  • Entertainment — every 3 hours at :30 (web search)
  • Tech — every 3 hours at :45 (web search)

The prompts are tuned to generate exactly 3 lines separated by |, with each line 30 characters or fewer. This took significant iteration — Haiku struggles to count characters reliably, so the prompts are explicit about it and include compliant examples.

Example entertainment automation action:

action: rest_command.claude_haiku_briefing
data:
  prompt: >
    Search the web for today's top entertainment or pop culture headline.
    Write display text for a retro split-flap airport board.
    CRITICAL RULES: Output ONLY the display text. Never explain, never hedge.
    ALL CAPS ONLY. EXACTLY 3 lines separated by pipe |.
    EACH LINE MUST BE 30 CHARACTERS OR FEWER — count every character
    including spaces carefully before outputting.
    Be punchy and opinionated — like a gossip columnist.
    No punctuation except pipe.
    Example: LOUIS CK RETURNS TO NETFLIX|HOLLYWOOD SAID NO NETFLIX YES|WILD TIMES IN STREAMING
response_variable: entertainment_response

The REST command points to the Anthropic API:

claude_haiku_briefing:
  url: "https://api.anthropic.com/v1/messages"
  method: POST
  timeout: 30
  headers:
    Content-Type: application/json
    x-api-key: !secret anthropic_api_key
    anthropic-version: "2023-06-01"
  payload: >
    {
      "model": "claude-haiku-4-5-20251001",
      "max_tokens": 256,
      "tools": [{"type": "web_search_20250305", "name": "web_search"}],
      "messages": [
        {
          "role": "user",
          "content": "{{ prompt | replace('\"', '\\\"') | replace('\n', '\\n') }}"
        }
      ]
    }

Stocks Slot

The stocks slot uses Sonnet (not Haiku) because it requires reasoning about financial context. It fires every hour at :15 during market hours (weekdays, 8:30am–3:05pm local time) and searches the web for the actual reason the S&P is moving that day.

At 3:30pm, a separate automation writes the final close number directly from a sensor — no API call needed:

action: input_text.set_value
target:
  entity_id: input_text.splitflap_stocks
data:
  value: >-
    {% set change = states('sensor.sp500_change') | float %}
    {% set direction = 'UP' if change >= 0 else 'DOWN' %}
    |MARKET NEWS:|S&P {{ direction }} {{ change | abs | round(2) }}%|MARKET CLOSED|

Packages Slot

The packages slot uses a 17track sensor and doesn’t call Claude at all — it’s pure Jinja templating.

Weather Alert Slot

Uses ai_task.generate_data (Haiku via the HA Anthropic integration) to analyze NWS alert sensor data and format it for the display. Only fires when an active alert is detected.


The News Headlines Card

The bottom section is a custom web component registered as an inline Lovelace resource. No files to upload — the code is embedded directly in HA.

It reads from feedparser sensors (the Feedparser custom integration) and displays 3 headlines side by side with images. Every 60 seconds it rotates to the next news source. There’s also a manual next button in the bottom right corner.

RSS Sensors (configuration.yaml)

sensor:
  - platform: feedparser
    name: "NPR News"
    feed_url: 'https://feeds.npr.org/1001/rss.xml'
    date_format: '%a, %d %b %Y %H:%M:%S %z'
    scan_interval:
      minutes: 30

  - platform: feedparser
    name: "The Verge"
    feed_url: 'https://www.theverge.com/rss/index.xml'
    date_format: '%a, %d %b %Y %H:%M:%S %z'
    scan_interval:
      minutes: 30

  - platform: feedparser
    name: "Deadline"
    feed_url: 'https://deadline.com/feed/'
    date_format: '%a, %d %b %Y %H:%M:%S %z'
    scan_interval:
      minutes: 30

  - platform: feedparser
    name: "The Hill"
    feed_url: 'https://thehill.com/feed/'
    date_format: '%a, %d %b %Y %H:%M:%S %z'
    scan_interval:
      minutes: 30

The Custom Card (Lovelace Resource)

Register this as an inline module resource in HA. The card handles image extraction across multiple RSS feed formats — enclosure links (The Hill), media_thumbnail arrays (Deadline), content HTML (NPR), and direct image fields (The Verge).

class NewsHeadlinesCard extends HTMLElement {
  setConfig(config) {
    this._config = config;
    this._lastState = null;
    this._currentIndex = 0;
    this._entities = config.entities || (config.entity ? [config.entity] : ['sensor.npr_news']);
    this._sources = config.source_names || this._entities;
    this._rotateInterval = (config.rotate_seconds || 60) * 1000;
  }

  connectedCallback() {
    this.innerHTML = `
      <style>
        .nh-wrap { position:relative; height:100%; }
        .nh-grid { display:grid; grid-template-columns:1fr 1fr 1fr; height:100%; background:#111; gap:0; }
        .nh-story { display:flex; flex-direction:column; border-right:1px solid #222; overflow:hidden; cursor:pointer; }
        .nh-story:last-child { border-right:none; }
        .nh-img { width:100%; aspect-ratio:16/9; object-fit:cover; display:block; flex-shrink:0; }
        .nh-body { padding:14px 16px; display:flex; flex-direction:column; gap:8px; }
        .nh-src { font-size:11px; font-weight:700; letter-spacing:.1em; color:#e8572a; text-transform:uppercase; }
        .nh-title { font-size:15px; font-weight:700; color:#fff; line-height:1.35; }
        .nh-summary { font-size:12px; color:rgba(255,255,255,.5); line-height:1.4; }
        .nh-next {
          position:absolute; bottom:16px; right:16px;
          width:44px; height:44px; border-radius:50%;
          background:rgba(255,255,255,0.1);
          border:1px solid rgba(255,255,255,0.2);
          display:flex; align-items:center; justify-content:center;
          cursor:pointer; z-index:10;
        }
        .nh-next svg { width:20px; height:20px; fill:rgba(255,255,255,0.8); }
      </style>
      <div class="nh-wrap">
        <div class="nh-grid" id="nh-grid"></div>
        <div class="nh-next" id="nh-next">
          <svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6z"/></svg>
        </div>
      </div>
    `;
    this.querySelector('#nh-next').onclick = function() { this._advance(); }.bind(this);
    if (this._hass) this._render(this._hass);
    this._startRotation();
  }

  disconnectedCallback() {
    if (this._timer) { clearInterval(this._timer); this._timer = null; }
  }

  _advance() {
    this._currentIndex = (this._currentIndex + 1) % this._entities.length;
    this._lastState = null;
    if (this._hass) this._render(this._hass);
    if (this._timer) clearInterval(this._timer);
    this._startRotation();
  }

  _startRotation() {
    if (this._entities.length <= 1) return;
    if (this._timer) clearInterval(this._timer);
    this._timer = setInterval(function() { this._advance(); }.bind(this), this._rotateInterval);
  }

  _extractImage(entry) {
    // 1. Enclosure links (The Hill, many WordPress sites)
    if (Array.isArray(entry.links)) {
      for (var i = 0; i < entry.links.length; i++) {
        var link = entry.links[i];
        if (link.rel === 'enclosure' && link.href && (link.type || '').indexOf('image') === 0) {
          return link.href;
        }
      }
    }
    // 2. media_thumbnail array (Deadline)
    if (entry.media_thumbnail && entry.media_thumbnail.length > 0 && entry.media_thumbnail[0].url) {
      return entry.media_thumbnail[0].url;
    }
    // 3. media_content array
    if (entry.media_content && entry.media_content.length > 0 && entry.media_content[0].url) {
      return entry.media_content[0].url;
    }
    // 4. image field (non-placeholder)
    if (entry.image && !entry.image.includes('favicon-192x192-full.png')) {
      return entry.image;
    }
    // 5. image embedded in content HTML (NPR)
    var contentVal = '';
    if (Array.isArray(entry.content) && entry.content.length > 0) {
      contentVal = entry.content[0].value || '';
    } else if (typeof entry.content === 'string') {
      contentVal = entry.content;
    }
    if (contentVal) {
      var m = contentVal.match(/<img[^>]+src=["']([^"']+)["']/i);
      if (m && m[1] && !m[1].includes('favicon') && !m[1].includes('tracking') && !m[1].includes('pixel')) return m[1];
    }
    return null;
  }

  _render(hass) {
    var grid = this.querySelector('#nh-grid');
    if (!grid || !this._config) return;
    var entityId = this._entities[this._currentIndex];
    var state = hass.states[entityId];
    if (!state) return;

    // Only re-render when sensor state actually changes
    var stateKey = this._currentIndex + ':' + state.state + state.last_updated;
    if (stateKey === this._lastState) return;
    this._lastState = stateKey;

    var entries = (state.attributes.entries || []).slice(0, 3);
    var sourceName = this._sources[this._currentIndex] || entityId;
    var self = this;
    grid.innerHTML = '';
    entries.forEach(function(e) {
      var story = document.createElement('div');
      story.className = 'nh-story';
      var img = self._extractImage(e);
      if (img) {
        var imgEl = document.createElement('img');
        imgEl.className = 'nh-img';
        imgEl.src = img;
        imgEl.onerror = function() { imgEl.remove(); };
        story.appendChild(imgEl);
      }
      var body = document.createElement('div');
      body.className = 'nh-body';
      body.innerHTML = '<div class="nh-src">' + sourceName + '</div>'
        + '<div class="nh-title">' + (e.title || '') + '</div>'
        + '<div class="nh-summary">' + (e.summary || '') + '</div>';
      story.appendChild(body);
      if (e.link) story.onclick = function() { window.open(e.link, '_blank'); };
      grid.appendChild(story);
    });
  }

  set hass(hass) {
    this._hass = hass;
    if (this.querySelector('#nh-grid')) this._render(hass);
  }

  getCardSize() { return 3; }
}

if (!customElements.get('news-headlines-card')) {
  customElements.define('news-headlines-card', NewsHeadlinesCard);
}
window.customCards = window.customCards || [];
window.customCards.push({
  type: 'news-headlines-card',
  name: 'News Headlines Card',
  description: '3-column rotating news headline display'
});

HACS Dependencies


Notes

On AI token usage: The Anthropic API has a 30,000 input tokens/minute rate limit. The staggered schedule (weather at :00, entertainment at :30, tech at :45) prevents the automations from colliding. Haiku is used for the splitflap slots (cheap, fast, good enough for short text). Sonnet is used only for the stocks analysis where reasoning quality matters.

On Haiku and character limits: Haiku is genuinely bad at counting characters. The prompts include explicit instructions and compliant examples, but occasional overflow still happens. word_wrap: false on the splitflap card helps — lines that overflow are clipped rather than wrapping onto extra rows, which at least keeps the layout predictable.

On the news card: The set hass setter fires constantly as HA updates state. The card uses a stateKey check (index + state + last_updated) to skip re-renders when nothing has changed — this was critical for preventing lag on older tablet hardware.

On tapping stories: When you tap a headline it opens in the browser via window.open(link, '_blank'). Combined with swipe navigation back to the dashboard, this creates a genuinely nice read-and-return flow on a wall-mounted tablet.

1 Like

Thanks again for the inspiration here; I’ve added another nice real-time energy monitor page to my collection. When I’m running entirely on solar energy, the first gauge turns green. And the middle gauge changes to medium purple when power (surplus) is supplied to the grid.

type: custom:custom-gauge-card

I really liked the simplicity and look and feel of your dashboard (however I would change the FCP badge for a Sporting one :smile:

Any chance you can share the Yaml code of your dashboard? Even with Claude coding for me, I’m struggling in alignments between columns and cards (I also have the navbar card) and I could learn something with yours! Thanks

Hi, your interface is magnificent, do you have a GitHub repository?