Ankündigung

Einklappen
Keine Ankündigung bisher.

HA - Newsticker (über Feedreader oder RESTful-API)

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    HA - Newsticker (über Feedreader oder RESTful-API)

    Heute gibt es einen 2-teiligen Beitrag der gleichzeitig auch einige (für mich) neue Techniken vorstellt.
    Ich zeige diesmal nicht nur das Ergebnis sondern auch den Weg dorthin, mal sehen, wie das hier ankommt.

    Im ersten Teil sehen wir, wie man sporadisch eintreffende Daten in einem Ringspeicher abgelegen kann,
    im zweiten Teil geht es darum, wie man Daten über die Rest-API beziehen kann.
    In beiden Teilen geht es aber um die grundsätzlich gleiche Aufgabe, einen Newsticker zu erstellen, nur halt mit unterschiedlichen Lösungsansätzen.

    Also worum geht's jetzt wirklich?

    Mein Dashboard zeigt ja schon sehr viel Info, nur der Badge-Bereich war bis jetzt noch leer.

    ​Viele zeigen dort Datum, Uhrzeit, Innen-/Außen-Temp. usw, aber für all das hab ich schon andere Plätze gefunden. Meine Idee war, den Badge-Bereich für News-Meldungen zu nutzen, links die letzte und rechts davon ältere.

    So sieht das ganze dann aus, wenn's fertig ist.

    ORF-News als Badge Leiste.png
    Ich hab bei der Erstellung des Codes viele Fehler gemacht und auch einige Rekursionen gedreht.
    Da man aus Fehlern lernen kann, möchte ich hier den Werdegang meines Newstickers vorstellen.

    Wer sich nur für das Ergebnis interessiert, an dem Werdegang aber kein Interesse hat, dem empfehle ich den Teil 1 komplett zu überspringen und gleich zu Teil 2 (im Post #2) weiterzugehen.

    Teil 1: Newsticker über Feedreader Integration
    Teil 2: Newsticker über Rest-API

    Für meinen Newsticker gibt es einen passenden RSS-Feed vom ORF und um diese Daten abzuholen ist der Feedreader wie geschaffen - dachte ich.

    Der Feedreader selbst war über das GUI auch sehr rasch eingerichtet, nur leider hat der erstellte Sensor gleich mehrere Nachteile.

    event.news_orf_at zeigt als State den timestamp der letzten Abholung und unter anderem folgende Attribute: description, title, link, content.

    Hier die Nachteile im Einzelnen:
    1. Ich bekomme immer nur genau die letzte Meldung im Sensor, alle Meldungen davor sind für mich weg, obwohl im RSS-Feed eigentlich immer die letzten 25 Meldungen drinnen bleiben würden.
    2. Das Feld content wird vom ORF nicht genutzt, das Feld description ist bei 90% der Meldungen leer (gut, dafür kann die Integration jetzt nichts)
    3. Das Feld <dc:subject> wäre für mich interessant gewesen (Inhalt z.B.: Inland, Ausland, Chronik, ...), leider kann die Integration nur die 4 genannten Felder abholen.
    4. Die Integration erlaubt keine direkte Änderung des Abhol-Intervalls, dafür gibt es aber eine Lösung.
    5. Der ORF verändert gemeinerweise auch ältere Meldungen im Nachhinein, was mein Code von Teil 1 gar nicht mag, der Teil2-Code hat dann aber keine Probleme damit.
    Lösungen zu 1 und 4 werde ich im Teil 1 hier beschreiben, die Lösung zu 3 kommt dann mit einem komplett anderen Ansatz in Teil 2.

    Als Lösung 1 hab ich einen Template-Trigger geschrieben, und obwohl der Code gar nicht so lang ist, hab ich ganz schön lang daran gearbeitet.

    Wenn ich von Anfang an gewusst hätte, dass ich das alles in die Tonne werfen werde, hätte ich mich nicht so bemüht, aber ich verbuche es einfach als Lernerfolg.

    Die größte Schwierigkeit beim Testen war, dass der Trigger nur dann zündet, wenn der ORF einen neuen Beitrag einstellt. Am besten macht man also noch irgendwas anderes (nicht zu spannendes) parallel dazu. Als Workaround hab ich den Trigger alle 2 min zünden lassen, musste dann aber als weiteren Workaround abfangen, wenn der eigentliche Event des Trigger leer war.

    Ich werde den Code dieses Mal etwas anders posten. Hier im Text eingebettet steht der Trigger mit seinen Test-Workarounds, im Anhang habe ich den Trigger von diesem Testcode bereinigt.

    Der folgende Code steht bei mir in der Datei template_triggers.yaml, daher fehlt auch das erste Schlüsselwort template:
    ​​
    HTML-Code:
    - triggers:
        - trigger: event
          event_type: feedreader
          event_data:
            feed_url: "https://rss.orf.at/news.xml"
        - trigger: time_pattern
          enabled: false
          minutes: "/2"
    Zuerst mal nur der Kopf des Triggers, der event trigger holt die echten Events ab und übergibt sie in der Variable trigger.event. Der time_pattern trigger ist für meine Testcases, trigger.event ist in diesem Fall aber dann leer. Hier ist dieser Trigger bereits disabled.

    HTML-Code:
      actions:
        - action: persistent_notification.create
          enabled: false
          data:
            title: "Feedreader Event"
            message: >
              {% set new_title = trigger.event.data.title if trigger.event is defined else "No Title" %}
              {% set new_link = trigger.event.data.link if trigger.event is defined else "https://orf.at/stories/0/" %}
              Feedreader hat eine neue ORF-News Meldung erhalten
              {{- '\n' -}}
              feed_url: {{ trigger.event.data.feed_url }}
              {{- '\n' -}}
              {{ new_title }}
              {{- '\n' -}}
              {{ new_link }}
              {{- '\n' -}}
              Zeitpunkt: {{ now().strftime('%Y-%m-%d %H:%M:%S') }}
    So eine Test-Action verwende ich gerne, da kann ich mir ausgeben lassen, welche Daten genau gekommen sind. enabled muss natürlich auf true stehen, sonst wird das übersprungen. Bei Time-Pattern hab ich dann den Dummy-Titel "No Title", damit funktioniert der Code auch ohne echtes trigger.event

    HTML-Code:
      sensor:
        - name: "News ORF Items aus Feedreader"
          unique_id: a89d28d7-43e2-4d92-95cb-a33cbf0605a5
          state: "{{ now().isoformat(timespec='seconds') }}"
          attributes:
            items: >
              {% set new_title = trigger.event.data.title if trigger.event is defined else "No Title" %}
              {% set new_link = trigger.event.data.link if trigger.event is defined else "https://orf.at/stories/0/" %}
              {% set current_items = state_attr('sensor.news_orf_items_aus_feedreader', 'items') or [] %}
              {% set links = current_items | map(attribute='link') | list %}
              {% if new_link not in links %}
                {% set new_datetime_iso = now().isoformat(timespec='seconds') %}
                {% set new_entry = {"title": new_title, "link": new_link, "zeitstempel": new_datetime_iso} %}
                {{ ([new_entry] + current_items)[:25] }}
              {% else %}
                {{ current_items }}
              {% endif %}
    In diesem Sensor hebe ich mir jetzt die letzten 25 Meldungen auf, zufällig genauso viele wie der ORF auch im RSS-Feed belässt.

    unique_id vergebe ich mittlerweile für fast alles, HA mag das einfach. Tipp für Studio-Code-Server: re-Maus & Generate UUID at Cursor

    Im State schreibe ich den aktuellen Timestamp im gleichen Format, wie die Integration das auch macht.
    Wer so wie ich mit den Zeitformaten immer noch durcheinander kommt, hier mein Schummelzettel dazu.

    ​In current_items hole ich den aktuellen Ringpuffer mit 25 Einträgen.

    In links hole ich nur die Links in dieser Liste, um zu prüfen, ob der neu gemeldete Eintrag wirklich neu ist.

    Wie ich oben schon geschrieben hab, verändert der ORF manchmal auch ältere Meldungen im Nachhinein. Das fange ich damit ab, indem ich die Links vergleiche und nicht die title. Bei mir bleibt dann halt der ursprüngliche Meldungstext im Ringpuffer, da ich ihn nicht ersetze.

    ([new_entry] + current_items)[:25] hängt den neuen Eintrag vorne dran und schneidet die Liste nach dem 25. wieder ab.

    Ich konnte in der Docu keine Info darüber finden, wie oft die Integration neue Daten holt, meiner Erfahrung nach war es ca. im Stundenintervall, dann aber gleich mehrere innerhalb von ms. Es gibt aber eine Möglichkeit das Intervall selbst in die Hand zu nehmen, und das habe ich auch gemacht.

    Achtung: Ich würde niemandem raten, dieser Anleitung "Defining a custom polling interval" zu folgen, ohne meine Warnung vorher gelesen zu haben.

    Das hat mich einiges an Nerven gekostet und ich möchte das anderen daher ersparen.

    Meine Anleitung hier anhand von good old YAML-Code kann jedenfalls gefahrlos übernommen werden.

    Der folgende Automation-Trigger steht bei mir jetzt in automations_manual.yaml, und es fehlt wie üblich das erste Schlüsselwort automation:

    HTML-Code:
    - alias: "Trigger News ORF Event"
      id: b8b273d5-08ea-4a57-ade8-c7d4664fbe1a
      description: Alle 10 min neue RSS-Feed Einträge abholen
      triggers:
        - trigger: time_pattern
          minutes: /10
      conditions: []
      actions:
        - action: homeassistant.update_entity
          data:
            entity_id:
              - event.news_orf_at
    Die unique_id heisst hier nur id, muss man nicht verstehen. Der Rest sollte eigentlich selbsterklärend sein.

    Nicht vergessen sollte man jedoch, in den "Systemoptionen für Feedreader" die "Abfrage von Änderungen" zu deaktivieren, denn sonst wird möglicherweise doppelt gezündet. Das ist der gefahrlose Teil der oberen Anleitung.

    So, das war's - nein, noch nicht ganz. Wir haben jetzt zwar die Daten in einem Ringpuffer alle 10 min aktualisiert, aber die Anzeige dazu fehlt ja noch.

    Auch das war für einen HTML-Neuling wie mich ganz schön aufwändig, aber diesen Teil konnte ich wenigstens für die Rest-Lösung wiederverwenden.

    Ich hab 2 Karten gebaut, eine in Form einer Badge-Leiste, die hab ich oben schon gezeigt, und eine Listenansicht, diese hier.

    ORF-News als chronologische Liste.png

    Ich zeige hier nur den Screenshot, der Code ist auch attached, aber vorstellen werde ich den Code erst im 2. Teil, da die Card dort noch etwas hübscher ist.

    Zusammenfassend kann ich sagen, dass ich mit dieser Lösung nicht vollständig zufrieden bin. Daher folgt jetzt auch noch eine bessere Lösung in Teil 2.
    Angehängte Dateien
    Zuletzt geändert von scw2wi; 13.11.2025, 19:28.

    #2
    Teil 2: Newsticker über Rest-API

    Mich hat die Lösung von Teil 1 nicht voll überzeugt. hier daher nochmals jene Nachteile, die ich im Teil 2 lösen möchte:
    • 3. Das Feld <dc:subject> wäre für mich auch noch interessant (Inhalt z.B.: Inland, Ausland, Chronik, ...),
      leider kann die Integration nur die 4 oben genannten Felder abholen.
    • 5. Der ORF verändert gemeinerweise auch ältere Meldungen im Nachhinein,
      damit kommt mein Code oben nicht in allen Fällen zurecht.
    • 6. Wenn der Ringpuffer leer ist, dann füllt er sich nur sehr langsam,
      obwohl im RSS-Feed immer 25 Meldungen vorhanden sind.
    Ich hab also weiter geforscht, wie man RSS-Feeds noch abholen kann, und hab gelesen, dass die REST Integration von HA nicht nur JSON sondern auch XML parsen kann.

    Super, der RSS-Feed ist ja XML codiert.

    Leider ist bei dieser Aufgabe ChatGPT noch vor der Lösung ausgestiegen, den Kurs "wie parse ich JSON Files" hat die KI wohl verschlafen. Daher habe ich mich selbst zur Lösung durchgearbeitet.

    Es gilt das gleiche wie auch oben. Wer am Lösungsweg interessiert ist kann jetzt einfach weiterlesen, wer nur das Ergebnis haben will, der scrollt einfach ans Ende. Im Attachment ist nur der Code der Ziel-Lösung, nicht die Zwischenschritte die ich hier beschreibe.

    Die im Teil 1 entwickelten Template-Trigger, Automation, usw. sind alle obsolet, wir beginnen also komplett von vorne.

    Der folgende Code des ersten Test-Aufrufs steht bei mir in rest.yaml, zum besseren Verständis steht in meinen Files trotzdem immer das relevante Schlüsselwort, aber halt auskommentiert.

    HTML-Code:
    # rest:
      - resource: https://rss.orf.at/news.xml
        scan_interval: 300 # alle 5 min
        headers:
          User-Agent: Home Assistant
          Content-Type: application/xml
        sensor:
          - name: "News ORF Feed via Rest"
            unique_id: e1061125-0988-4e9f-8269-33779d8bba74
            value_template: "{{ (value_json | tojson)[:255] }}"
    Beim ersten Test-Aufruf parse ich den XML/JSON-Code noch nicht sondern hole nur die ersten 255 char (da der Status auf 255 char begrenzt ist) zur weiteren Analyse unverändert in den Status des Sensors.

    Wenn man das Limit auf 255 char weglässt, dann wird der gesamte String im Log-File als Error ausgegeben, auch nicht schlecht. Der Sensor selbst bleibt dann aber undefiniert.

    Kurzer Ausflug zum Thema "Log-File".

    Obwohl ich noch auf HA 2025.10 bin bereite ich mich jetzt schon darauf vor, wie ich nach dem Update auf 2025.11 das Log-File aufrufen kann.

    Derzeit verwende ich das Log-Viewer Add-on, das aber genau jenes Logfile anzeigt, die es nach dem Update nicht mehr geben wird.

    Ich werde mir daher angewöhnen, im Studio Code Server mit "Toggle Panel" (Ctrl-J) das Terminal einzublenden, und dort mit dem Befehl "ha core logs" das Logfile anzuzeigen.

    Mit dem Parameter -f wird es sogar laufend angezeigt, bis man das mit Ctrl-C wieder stoppt.

    Langsam (sehr langsam) aber sicher lerne ich ja doch noch ein paar Linux Befehle. Wer damit nicht zurecht kommt, der kann sich ja a-file-logger ​ansehen.​

    </Log-File-Ausflug>

    Headers benötigt man hier eigentlich nicht, daher kann man das auch weglassen. Bei manchen Rest-APIs wird jedoch noch viel mehr benötigt, z.B. authentication usw.

    Im Sensor-Status kann ich mir jetzt den von XML nach JSON umgewandelten String ansehen und das nächste tag identifizieren, das ich parsen will. So komme ich Schritt für Schritt zum Ergebnis.

    HTML-Code:
            # value_template: "{{ (value_json['rdf:RDF'] | tojson)[:255] }}"
            # value_template: "{{ (value_json['rdf:RDF']['channel'] | tojson)[:255] }}"
            # value_template: "{{ (value_json['rdf:RDF']['channel']['dc:date'] | tojson)[:255] }}"
            # value_template: "{{ (value_json['rdf:RDF']['item'] | tojson)[:255] }}"
    ['channel']['dc:date'] hab ich wieder verworfen, damit wollte ich ursprünglich jenes Datum in den Status übernehmen, zu dem der ORF den letzten Eintrag hinzugefügt hat.

    Die gleiche Info hab ich aber ohnehin im neuesten Feed-Eintrag, daher werde ich im Sensor-Status gar keinen Wert auf dem RSS-Feed sondern einfach den aktuellen Timestamp speichern. Der wird dann in der Card als "Zuletzt aktualisiert" angezeigt.

    ['rdf:RDF']['item'] Dieser Inhalt sind alle 25 Feeds mit allen Feldern, das schreibe ich in ein Attribut, dort gibt es kein 255 char Limit.

    Mit dieser Info hab ich jetzt einen Entwurf, der die Daten bereits korrekt übernimmt.

    HTML-Code:
        sensor:
          - name: "News ORF Feed via Rest"
            unique_id: e1061125-0988-4e9f-8269-33779d8bba74
            value_template: "{{ now().isoformat(timespec='seconds') }}"
            json_attributes_path: "$['rdf:RDF']"
            json_attributes:
              - "item"
    Der Status des Sensors speichert jetzt den timestamp der letzten Aktualisierung. Die Format-Umwandlung hätte ich auch lassen können, im ISO-Format passt es aber besser zu den anderen timestamps hier.

    Der selector "json_attributes" wählt alle 'item' Elemente aus und speichert sie (mit allen Tags darin) als Liste im Sensor-Attribut item.
    Es ist an dieser Stelle einfacher, alle zu speichern, und erst in der weiteren Verarbeitung jene zu ignorieren, die ich dann doch nicht benötige.

    Im nächsten Schritt werde ich dieses Attribut "item" auf 2 verschiedene Arten aufbereiten.
    Einmal für die Badge, da benötige ich die jeweils die 3 neuesten Meldungen in einzelnen Attributen,
    und einmal für die Liste, da werde ich genauso wie im Teil 1 gleich einen HTML-String erzeugen, den ich mit der custom:html-template-card 1:1 ausgeben kann.

    Zur Aufbereitung verwende ich, wie könnte es anders sein, einen Template-Sensor, und speichere beide Ergebnisse als zwei Attribute im gleichen Sensor.

    Hier mal der erste Teil des Codes aus meinem File template_sensor.yaml, daher wieder ohne die Zeile template:

    HTML-Code:
    # template:
    - sensor:
        - name: "News ORF Feed via Rest formatted"
          # sensor.news_orf_feed_via_rest_formatted
          unique_id: 305532f4-936b-4f5d-92d0-ba6dfaad73fc
          icon: mdi:newspaper-variant-outline
          state: "{{ states('sensor.news_orf_feed_via_rest') }}" # datestamp aus RSS-Feed, wird direkt übernommen
          attributes:
            # plain list aller items (verwendet für die Anzeige der letzten 3 Meldungen im Badge)
            items: >
              {% set ns = namespace(combined=[]) %}
              {% set raw_items = state_attr('sensor.news_orf_feed_via_rest', 'item') %}
              {% if (raw_items is not none) and (raw_items | length > 0) %}
                {% for item in raw_items %}
                  {% set entry = {
                    'title': item.title,
                    'link': item.link,
                    'zeitstempel': item['dc:date'],
                    'subject': item['dc:subject']
                  } %}
                  {% set ns.combined = ns.combined + [entry] %}
                {% endfor %}
                {{ ns.combined }}
              {% else %}
                []
              {% endif %}
            formatted_html: >
              ...
    Den Trick mit dem namespace kenne ich seit meiner Card mit den Flugbewegungen, nur die KI lässt in ihrem Code den namespace meistens weg, bis man sie darauf hinweist, dass es ohne nicht funktioniert.

    Hier hole ich mir jetzt nur jene 4 items (title, link, ['dc:date'] und ['dc:subject']) die ich tatsächlich benötige aber nicht nur (wie geplant) die letzten 3 Meldungen, sondern gleich alle - zu viel Info stört da ja nicht wirklich.

    Weiter geht's mit dem zweiten Teil des Sensors. Ich möchte die Meldungen nicht mehr in einer flachen Liste ausgeben, sondern nach Subject (das sind die Themen wie Inland, Ausland, ...) gruppieren, und je Gruppe auf 3 Einträge begrenzen.

    HTML-Code:
              ...
            formatted_html: >
              {% set items = state_attr('sensor.news_orf_feed_via_rest', 'item') or [] %}
              {% set priority = ['Inland', 'Ausland', 'Chronik'] %}
              {% set filtered_items = items | selectattr('dc:subject', 'defined') | list %}
              {% set filtered_items = filtered_items | rejectattr('dc:subject', 'search', 'Lotto') | list %}
              {% set filtered_items = filtered_items | rejectattr('dc:subject', 'equalto', 'auszublenden') | list %}
              {% set subjects = filtered_items | map(attribute='dc:subject') | list %}
              {% set unique_subjects = subjects | unique | list %}
              {% set sorted_subjects = priority + (unique_subjects | reject('in', priority) | sort) %}
    
              {% set ns = namespace(output='') %}
              ...
    Mit priority kann ich festlegen, wie die Gruppen für die Anzeige sortiert sein sollen.

    Mit rejectattr kann ich Gruppen komplett ausblenden, das Subject Lotto z.B. interessiert mich nicht. Entweder wird nach einem Teilstring gesucht, oder (bei equalto) auf exaktes Matching verglichen.

    sorted_subjects sortiert zuerst anhand der priority-Liste, danach dann alphabetisch.

    Weiter geht's wieder mit einem namespace, der sicherstellt, dass die Variable output innerhalb und außerhalb der Schleife identisch ist. (Sonst wären es 2 getrennte Variablen, und dann geht die Logik nicht.)

    HTML-Code:
              ...
              {% set ns = namespace(output='') %}
              {% for subject in sorted_subjects %}
                {% set entries = filtered_items | selectattr('dc:subject', 'equalto', subject) | sort(attribute='dc:date', reverse=true) | list %}
                {# auf max. 3 Einträge je Subject begrenzen #}
                {% set entries = entries[:3] %}
                {% if entries %}
                  {% set ns.output = ns.output ~ '
                    <div style="font-size: 20px; font-weight: 400; margin-top: 0px;">' ~ subject ~ '</div>' %}
                  {% for item in entries %}
                    {% set ts = item['dc:date'] | as_datetime(default=now()) | as_timestamp(default=0) %}
                    {% if ts > 0 %}
                      {% set time_hhmm = ts | timestamp_custom('%H:%M') %}
                      {% set line_html = '
                        <div style="margin-bottom: 3px; display: flex; align-items: flex-start;">
                          <div style="flex-shrink: 0; white-space: nowrap; margin-right: 10px;">
                            <span style="color: grey; position: relative; top: -6px;">' ~ time_hhmm ~ '</span> &nbsp;
                            <a href="' ~ item.link ~ '" target="_blank" rel="noopener noreferrer">
                              <span style="font-size: 24px; position: relative; top: -4px;"></span>
                            </a>
                          </div>
                          <div style="font-size: 18px; font-weight: 500; flex-grow: 1; word-break: break-word;">
                            ' ~ item.title ~ '
                          </div>
                        </div>' %}
                      {% set ns.output = ns.output ~ line_html ~ '\n' %}
                    {% endif %}
                  {% endfor %}
                {% endif %}
              {% endfor %}
              {{ ns.output }}
              <br>
              ...
    Ich habe der KI genau beschrieben, wie die Ausgabe aussehen soll, und sie hat mir diesen HTML-Code erstellt. Ich hätte 1000 mal länger gebraucht dafür - nein, stimmt nicht, in 15 min hätte ich das noch nicht gehabt.

    HTML-Code:
              ...
              <br>
              <div style="font-size: 14px; color: grey;">Zuletzt aktualisiert:
              {% if states('sensor.news_orf_feed_via_rest') | as_timestamp(default=0) > 0 %}
                {% set weekdays_de = ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"] %}
                {% set months_de = ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"] %}
                {% set datetime = states('sensor.news_orf_feed_via_rest') | as_datetime %}
                {{ weekdays_de[datetime.weekday()] }}, {{ datetime.day }}. {{ months_de[datetime.month - 1] }} {{ datetime.strftime('%H:%M') }}
              {% else %}
                Zeitpunkt unbekannt
              {% endif %}
              </div>
    Der Footer ist dagegen wieder trivial. Ich hab bis heute keine einfachere Möglichkeit gefunden, sich in HA ein Datum in der local language ausgeben zu lassen. Bei vielen Sensor-States funktioniert das problemlos. Entweder hab ich noch nicht genug gesucht danach, oder es gibt keine bessere Lösung als diesen Workaround. Mit dem Easy Time Makro wäre es zwar machbar, aber das ist für diese Aufgabe etwas oversized.

    ​OK, das war der schwierige Teil der Aufgabe, jetzt kommt der einfache, die beiden Cards für das Dashboard.

    Für die Badge Ansicht habe ich die mushroom-template-card 3x in ein grid layout gepackt und mit "margin: 0 1em 0 1em" Abstände dazwischen eingefügt. Ein geeignetes ORF_news Logo findet man leicht, man kann aber auch nur ein icon anzeigen.

    ORF-News als Badge Leiste.png
    Mit tap_action navigiere ich auf eine Unterseite, wo dann die nächste Card alle Meldungen gruppiert anzeigt.

    Auf dieser Unterseite wird die Badge nochmals (mit identischer Ansicht) dargestellt, diesmal aber mit tap_action zurück zum Dashboard.

    HTML-Code:
    type: custom:layout-card
    layout_type: grid
    layout:
      grid-template-columns: 1fr 1fr 1fr
    cards:
      - type: custom:mushroom-template-card
        primary: >
          {{ state_attr('sensor.news_orf_feed_via_rest_formatted', 'items')[0].title
          }}
        secondary: >
          {% set timestamp_mitT = state_attr('sensor.news_orf_feed_via_rest_formatted', 'items')[0].zeitstempel %}
          {% set timestamp_ohneT = timestamp_mitT | as_datetime %}
          {% set timestamp_unix = timestamp_ohneT | as_timestamp %}
          {% set time_hhmm = timestamp_unix | timestamp_custom('%H:%M') %}
          {{ time_hhmm }} -
          {{ state_attr('sensor.news_orf_feed_via_rest_formatted', 'items')[0].subject }}
        icon: mdi:newspaper
        picture: local/images/ORF_news.png
        features_position: bottom
        tap_action:
          action: navigate
          navigation_path: /home/news
        card_mod:
          style:
            ha-tile-info$: |
              .primary {
                font-size: 1.2em !important;
              }
            .: |
              ha-card {
                margin: 0 1em 0 1em;
              }
      - type: custom:mushroom-template-card
        ...
    Ursprünglich hatte ich diese Badge tatsächlich im oberen Badge-Bereich, viel besser gefällt sie mir jedoch als Footer, und der Card macht das ja nichts aus, da sie keine echte Badge-Card ist. Als Footer habe ich einfach einen neuen Abschnitt mit Breite 4 angelegt.

    Die nächste und letzte Card zeigt jetzt die Meldungen gruppiert nach Thema. Hier zeige ich nur den Code der Liste, im Attachment gibt es auch noch (über die vertical-stack card) einen Header darüber.

    Diese Card zeige ich in einem Abschnitte der Breite 2 an, also über den halben Bildschirm. Daneben werden die Heise News Meldungen angezeigt, die ich ganz easy über die Heise.de Integration bekomme.

    ORF-News als gruppierte Liste.png

    HTML-Code:
        ...
      - type: custom:html-template-card
        title: ""
        ignore_line_breaks: false
        always_update: false
        picture_elements_mode: false
        entities: []
        content: >-
          {{ state_attr('sensor.news_orf_feed_via_rest_formatted', 'formatted_html')
          }}
    grid_options:
      columns: full
    Insgesamt hab ich 2 Wochenenden daran gearbeitet und viele Bugs in meinem Code gehabt, aber ich hab auch viel dabei gelernt, und das ist auch der Grund, warum ich mich entschlossen hab, diesmal auch den Lösungsweg hier zu beschreiben.

    Mal sehen, wann der Erste mit dieser Lösung auch die Tagesschau abruft. Anhand der Anleitung ist es sicher recht einfach zu übertragen.

    ​Da es mich interessiert, wie gut so ein ausführlicher Beitrag hier ankommt, hätte ich eine kleine Bitte an jene, die meine HA-Beiträge gerne liken.

    Like nur auf Teil 1 bedeutet: Der Lösungsweg war zwar interessant, mit der Lösung selbst kann ich aber nichts anfangen.
    Like nur auf Teil 2 bedeutet: Die Lösung kann ich gut verwenden, der Lösungsweg interessiert mich aber nicht.
    Like auf beide bedeutet: na was wohl?

    Je nachdem, wie das Ergebnis ausfällt, werde ich so etwas wiederholen, oder auch nicht.

    Weitere Beiträge dieser Serie:

    HACS Wetterkarte
    HACS Sonne & Mond (inkl. Tipps zur configuration.yaml)
    HACS Schieberegler (inkl. Möglichkeiten der Icon Farbanpassung & erste Vorstellung Farbschema)​
    HACS Gauges (Tachoanzeigen, inkl. senkrechte Balken-Cards)​
    HACS stack-in-card für Raum-Card
    HACS Graph-Cards
    HACS Thermostat-Cards
    ​​HACS Entity Cards (inkl. stacking Beispiele mit der custom:button-card)
    HACS Reminder (trash-card, atomic-calendar-revive)​
    HACS Person- & Öffi-Card (inkl. Geschichte zu ChatGPT)​​​​
    HA-Behaglichkeits-Diagramm
    HA Kurzeinführung
    HACS Sidebar & Dashboard-Entwurf
    HACS Bewässerung​
    HA & Node-Red
    HA Notifications
    HA Recorder, SQLite, InfluxDB, Grafana
    HA Python Scripts
    HACS Browser_Mod
    Angehängte Dateien
    Zuletzt geändert von scw2wi; 13.11.2025, 19:13.

    Kommentar

    Lädt...
    X