📰 Lovelace: RSS Feed Parser Plugin/List Card

https://github.com/iantrich/list-card says : Installation: use HACS

Yeah but you have to add it as a custom repo in hacs and it’s always been this way. Add that repo and you can install using Hacs.

1 Like

Excuse me for my ignorance, but I don’t understand your answer. This is what I do,
I go to HACS in my home assistant (core) I select frontend and press the + button to add a repository

Then I try to find the list card but it is not there. What do I have to do more ?
I installed all hacs this way

Thank you, did not know that one, should pay more attention to manuals .

I just found out the feedparser and the list-card and would love to get it working, I’ve got the feedparser working but I dont want to install HACS, I have a microSD image-based hassio install and dont want to fiddle with the host OS. Isn’t there any manual installation method like putting files into custom_components or so?

or registering the .js through the hassio GUI?

Ok I figured how to install the js-file manually. Got everything working except it doesnt get the “description” variable. Only image and title works?

Also pretty few rss feeds seem to use the “image”. at least none of the old ones I use :confused:

Things I've learned using sensor.feedparser and custom:list-card

Here is what the original rss feed (https://www.bonappetit.com/feed/latest-recipes/rss) looks like when viewed using Chrome. (Just showing one item.)

<item>
<title>Rustic Shrimp Toasts</title>
<link>https://www.bonappetit.com/recipe/rustic-shrimp-toasts</link>
<guid isPermaLink="false">5fb84e3534f1071aa506abda</guid>
<pubDate>Mon, 18 Jan 2021 14:00:00 +0000</pubDate>
<media:content/>
<description>Shrimp tossed in garlic mayonnaise and griddled on thick bread—as good as a late-night snack, as a handheld appetizer, or a quick weeknight dinner.</description>
<category>recipes</category>
<media:keywords>Shrimp, Seafood, Shellfish, Green Onion Scallion, Capers, Kosher Salt, Aioli, Olive Oil, Parsley, Lemon</media:keywords>
<dc:creator>Christian David Reynoso</dc:creator>
<dc:modified>Wed, 20 Jan 2021 15:29:33 +0000</dc:modified>
<dc:publisher>Condé Nast</dc:publisher>
<media:thumbnail url="https://assets.bonappetit.com/photos/5fd7a99f1978a0aa6459b6c4/master/pass/Shrimp-Toast-Recipe-Lede.jpg" width="2000" height="1125"/>
</item>

When you first start working with feedparser DO NOT use any inclusions or exclusions.

  - platform: feedparser
    name: Bon Appetit All
    feed_url: 'https://www.bonappetit.com/feed/latest-recipes/rss'

This created sensor.bon_appetit_all.

I viewed the sensor data using Developer Tools: STATES. It displayed all the items and all the ‘keys’. (Showing only one item below.)

As you can see, the parsed feed does not match the <item> in Chrome. (One example is description is replaced with summary.)

  - title: Rustic Shrimp Toasts
    title_detail:
      type: text/plain
      language: null
      base: 'https://www.bonappetit.com/feed/latest-recipes/rss'
      value: Rustic Shrimp Toasts
    links:
      - rel: alternate
        type: text/html
        href: 'https://www.bonappetit.com/recipe/rustic-shrimp-toasts'
    link: 'https://www.bonappetit.com/recipe/rustic-shrimp-toasts'
    id: 5fb84e3534f1071aa506abda
    guidislink: false
    published: 'Mon, Jan 18 02:00 PM'
    media_content:
      - {}
    summary: >-
      Shrimp tossed in garlic mayonnaise and griddled on thick bread—as good as
      a late-night snack, as a handheld appetizer, or a quick weeknight dinner.
    summary_detail:
      type: text/html
      language: null
      base: 'https://www.bonappetit.com/feed/latest-recipes/rss'
      value: >-
        Shrimp tossed in garlic mayonnaise and griddled on thick bread—as good
        as a late-night snack, as a handheld appetizer, or a quick weeknight
        dinner.
    tags:
      - term: recipes
        scheme: null
        label: null
      - term: Shrimp
        scheme: null
        label: null
      - term: Seafood
        scheme: null
        label: null
      - term: Shellfish
        scheme: null
        label: null
      - term: Green Onion Scallion
        scheme: null
        label: null
      - term: Capers
        scheme: null
        label: null
      - term: Kosher Salt
        scheme: null
        label: null
      - term: Aioli
        scheme: null
        label: null
      - term: Olive Oil
        scheme: null
        label: null
      - term: Parsley
        scheme: null
        label: null
      - term: Lemon
        scheme: null
        label: null
    media_keywords: >-
      Shrimp, Seafood, Shellfish, Green Onion Scallion, Capers, Kosher Salt,
      Aioli, Olive Oil, Parsley, Lemon
    authors:
      - name: Christian David Reynoso
    author: Christian David Reynoso
    author_detail:
      name: Christian David Reynoso
    dc_modified: 'Wed, 20 Jan 2021 15:29:33 +0000'
    publisher: Condé Nast
    publisher_detail:
      name: Condé Nast
    media_thumbnail:
      - url: >-
          https://assets.bonappetit.com/photos/5fd7a99f1978a0aa6459b6c4/master/pass/Shrimp-Toast-Recipe-Lede.jpg
        width: '2000'
        height: '1125'
    href: ''

I used the Developers Tools: TEMPLATE to look at the data. (Here are two examples
)

{{ states.sensor.bon_appetit_all.attributes.entries[0]['media_thumbnail'][0]}}

{
  "url": "https://assets.bonappetit.com/photos/5fd7a99f1978a0aa6459b6c4/master/pass/Shrimp-Toast-Recipe-Lede.jpg",
  "width": "2000",
  "height": "1125"
}
{{ states.sensor.bon_appetit_all.attributes.entries[0]['media_thumbnail'][0]['url'] }}

https://assets.bonappetit.com/photos/5fd7a99f1978a0aa6459b6c4/master/pass/Shrimp-Toast-Recipe-Lede.jpg

I wanted to use the media_thumbnail url as my image so replaced the following code in /custom_components/feedparser/sensor.py (Creating a backup copy of the original first.)

            if 'image' in self._inclusions and 'image' not in entryValue.keys():
                images = []
                if 'summary' in entry.keys():
                    images = re.findall(r"<img.+?src=\"(.+?)\".+?>", entry['summary'])
                if images:
                    entryValue['image'] = images[0]
                else:
                    entryValue['image'] = "https://www.home-assistant.io/images/favicon-192x192-full.png"

With this


            if 'image' in self._inclusions and 'image' not in entryValue.keys():
                images = []
                if 'summary' in entry.keys():
                    images = re.findall(r"<img.+?src=\"(.+?)\".+?>", entry['summary'])
                if images:
                    entryValue['image'] = images[0]
                else:
                    if 'media_thumbnail' in entry.keys():
                        images = re.findall("\'url\': \'(.+?)\'", str(entry['media_thumbnail']))
                    if images:
                        entryValue['image'] = images[0]
                    else:
                        entryValue['image'] = "https://www.home-assistant.io/images/favicon-192x192-full.png"

The code first tests for the ‘image’ key and if not found, will test for the ‘media_thumbnail’ key and extract the ‘url’.

Using the sensor data I rewrote my configuration.yaml file for the sensor.

  - platform: feedparser
    name: Bon Appetit
    feed_url: 'https://www.bonappetit.com/feed/latest-recipes/rss'
    inclusions:
      - title
      - link
      - published
      - summary
      - image
    exclusions:
      - title_detail
      - links
      - id
      - guidislink
      - media_content
      - summary_detail
      - tags
      - media_keywords
      - authors
      - author
      - author_detail
      - dc_modified
      - publisher
      - publisher_detail
      - href

This created sensor.bon_appetit. (Again showing only one item below.)

  - title: Rustic Shrimp Toasts
    link: 'https://www.bonappetit.com/recipe/rustic-shrimp-toasts'
    published: 'Mon, Jan 18 02:00 PM'
    summary: >-
      Shrimp tossed in garlic mayonnaise and griddled on thick bread—as good as
      a late-night snack, as a handheld appetizer, or a quick weeknight dinner.
    image: >-
      https://assets.bonappetit.com/photos/5fd7a99f1978a0aa6459b6c4/master/pass/Shrimp-Toast-Recipe-Lede.jpg

Using this sensor data you can create your custom:list-card.

type: 'custom:list-card'
entity: sensor.bon_appetit
title: Bon Appetit
feed_attribute: entries
columns:
  - title: ''
    type: image
    add_link: link
    field: image
  - title: ''
    field: title
    add_link: link

I think better solution would be to include a - thumbnail key so you could extract both an image or a thumbnail and have the custom:list-card display both the - image key and/or the -thumbnail key.

sensor.py uses ‘feedparser’, some great documentation can be found here: feedparser documentation.

In the case of the Bon Appetit feed the thumbnail images don’t have a great look and I probably won’t be using them, but it was a good learning experience.

Hope that helps some folks


5 Likes

Hello,
My RSS feeds looks a little bit different.
I have found the image url in DEV tools, it is like this: {{ states.sensor.vrt_nws.attributes.entries[0]['links'][2]['href'] }}
How can I get this in sensor.py file ?
I am not a programmer.
Thanks for your help !
Bart

Could you post the feed url?

Would be easier for me to figure it out.

You make adjustments to the sensor.py file pretty much the same way you make manual changes to the configuration.yaml file.

The sensor.py file is found in “/config/custom_components/feedparser/sensor.py”

For a lot of the quick work I use “File Editor” installed from “Supervisor: Add-on Store”.

Hope I can help.

Think I found the rss feed:

  - platform: feedparser
    name: VRT NWS
    feed_url: 'https://www.vrt.be/vrtnws/en.rss.articles.xml'
    date_format: '%b %d'
    inclusions:
      - title
      - link
      - published
      - summary
      - image

I updated the code in sensor.py to look like this and the image is displayed.

                if 'image' in self._inclusions and 'image' not in entryValue.keys():
                    images = []
                    if 'summary' in entry.keys():
                        images = re.findall(r"<img.+?src=\"(.+?)\".+?>", entry['summary'])
                    if images:
                        entryValue['image'] = images[0]
                    else:
                        if 'links' in entry.keys():
                            images = re.findall("\'href\': \'(.+?)\'", str(entry['links']))
                        if images:
                            entryValue['image'] = images[2]
                        else:
                            entryValue['image'] = "https://www.home-assistant.io/images/favicon-192x192-full.png"

It is written in python so the spaces before the code have to match. In this case they are spaces and not tabs


I would be sure to copy and paste and create a copy of the original sensor.py before changing any of the code.

Hope this helps


Thanks alot that was the solution !
Is it also possible to change the width of the image ?

I’m trying to figure that out, but have had no luck.

Will post if I come up with a solution.

It has to do with the custom:list-card, so digging into that.

Ok, here is one way to get the pictures to show wider


Using File editor:

Select the Folder Icon to “Browse Filesystem”.
Scroll down to www and click into the folder.
Select the File + Icon to create “New file”.

Name the new file list-card-wide.js

Select the new file and paste the following code into the list-card-wide.js file and save.

class ListCardWide extends HTMLElement {

    constructor() {
      super();
      this.attachShadow({ mode: 'open' });
    }

    setConfig(config) {
      if (!config.entity) {
        throw new Error('Please define an entity');
      }

      const root = this.shadowRoot;
      if (root.lastChild) root.removeChild(root.lastChild);

      const cardConfig = Object.assign({}, config);
      const columns = cardConfig.columns;
      const card = document.createElement('ha-card');
      const content = document.createElement('div');
      const style = document.createElement('style');
      style.textContent = `
            ha-card {
              /* sample css */
            }
            table {
              width: 100%;
              padding: 0 0px 0px 0px;
            }
            thead th {
              text-align: left;
            }
            tbody tr:nth-child(odd) {
              background-color: var(--paper-card-background-color);
            }
            tbody tr:nth-child(even) {
              background-color: var(--secondary-background-color);
            }
            .button {
              overflow: auto;
              padding: 0px;
            }
            paper-button {
              float: right;
            }
            td a {
              color: var(--primary-text-color);
              text-decoration-line: none;
              font-weight: normal;
            }
          `;

      // Go through columns and add CSS sytling to each column that is defined
      if (columns) {
        for (let column in columns) {
          if (columns.hasOwnProperty(column) && columns[column].hasOwnProperty('style')) {
            let styles = columns[column]['style'];

            style.textContent += `
              .${columns[column].field} {`

            for (let index in styles) {
              if (styles.hasOwnProperty(index)) {
                for (let s in styles[index]) {
                  style.textContent += `
                  ${s}: ${styles[index][s]};`;
                }
              }
            }

            style.textContent += `}`;
          }
        }
      }

      content.id = "container";
      cardConfig.title ? card.header = cardConfig.title : null;
      card.appendChild(content);
      card.appendChild(style);
      root.appendChild(card);
      this._config = cardConfig;
    }

    set hass(hass) {
      const config = this._config;
      const root = this.shadowRoot;
      const card = root.lastChild;

      if (hass.states[config.entity]) {
        const feed = config.feed_attribute ? hass.states[config.entity].attributes[config.feed_attribute] : hass.states[config.entity].attributes;
        const columns = config.columns;
        this.style.display = 'block';
        const rowLimit = config.row_limit ? config.row_limit : Object.keys(feed).length;
        let rows = 0;

        if (feed !== undefined && Object.keys(feed).length > 0) {
          let card_content = '<table><thread><tr>';

          if (!columns) {
            card_content += `<tr>`;

            for (let column in feed[0]) {
              if (feed[0].hasOwnProperty(column)) {
                card_content += `<th>${feed[0][column]}</th>`;
              }
            }
          } else {
            for (let column in columns) {
              if (columns.hasOwnProperty(column)) {
                card_content += `<th class=${columns[column].field}>${columns[column].title}</th>`;
              }
            }
          }

          card_content += `</tr></thead><tbody>`;

          for (let entry in feed) {
            if (rows >= rowLimit) break;

            if (feed.hasOwnProperty(entry)) {
              if (!columns) {
                for (let field in feed[entry]) {
                  if (feed[entry].hasOwnProperty(field)) {
                    card_content += `<td>${feed[entry][field]}</td>`;
                  }
                }
              } else {
                let has_field = true;

                for (let column in columns) {
                  if (!feed[entry].hasOwnProperty(columns[column].field)) {
                    has_field = false;
                    break;
                  }
                }

                if (!has_field) continue;
                card_content += `<tr>`;

                for (let column in columns) {
                  if (columns.hasOwnProperty(column)) {
                    card_content += `<td class=${columns[column].field}>`;

                    if (columns[column].hasOwnProperty('add_link')) {
                      card_content +=  `<a href="${feed[entry][columns[column].add_link]}" target='_blank'>`;
                    }

                    if (columns[column].hasOwnProperty('type')) {
                      if (columns[column].type === 'image') {
                        if (feed[entry][columns[column].field][0].hasOwnProperty('url')) {
                            card_content += `<img src="${feed[entry][columns[column].field][0].url}" width="130" height="90" style="vertical-align:middle">`;
                        } else {
                          card_content += `<img src="${feed[entry][columns[column].field]}" width="130" height="90" style="vertical-align:middle">`;
                        }
                      }
                      // else if (columns[column].type === 'button') {
                      //   card_content += `<paper-button raised>${feed[entry][columns[column].button_text]}</paper-button>`;
                      // }
                    } else {
                      let newText = feed[entry][columns[column].field];

                      if (columns[column].hasOwnProperty('regex')) {
                        newText = new RegExp(columns[column].regex).exec(feed[entry][columns[column].field]);
                      } 
                      if (columns[column].hasOwnProperty('prefix')) {
                        newText = columns[column].prefix + newText;
                      } 
                      if (columns[column].hasOwnProperty('postfix')) {
                        newText += columns[column].postfix;
                      }

                      card_content += `${newText}`;
                    }

                    if (columns[column].hasOwnProperty('add_link')) {
                      card_content +=  `</a>`;
                    }

                    card_content += `</td>`;
                  }
                }
              }

              card_content += `</tr>`;
              ++rows;
            }
          }

          root.lastChild.hass = hass;
          card_content += `</tbody></table>`;
          root.getElementById('container').innerHTML = card_content;
        } else {
          this.style.display = 'none';
        }
      } else {
        this.style.display = 'none';
      }
    }

    getCardSize() {
      return 1;
    }
  }

  customElements.define('list-card-wide', ListCardWide);

Next, go to Configuration : Lovelace Dashboards and select “Resources” up at the top.

Select the + Add Resource button in the lower right.

The url is /local/list-card-wide.js
JavaScript Module will automatically be selected.

Do a Configuration Validation and restart the server.

Below is a sample card I created with just some simple data


type: 'custom:list-card-wide'
entity: sensor.vrt_nws
title: VRT News
feed_attribute: entries
columns:
  - title: ''
    type: image
    field: image
  - title: ''
    field: title
    add_link: link
  - title: ''
    field: published

All I did was copy the original custom:list-card and change the html around the img tag.

card_content += `<img src="${feed[entry][columns[column].field][0].url}" width="130" height="90" style="vertical-align:middle">`;

Hopefully there will be some updates to the original list-card that can incorporate some of my hacks.
I’m no programmer and am very new to Home Assistant, but learning quickly


I just wanted to throw out there the use of the Markdown card with the Feed Parser data.

It helps create very nice display for the data.

  - type: markdown
    content: >-
      ---

      ## [{{ states.sensor.bon_appetit.attributes.entries[0]['title'] }}]({{
      states.sensor.bon_appetit.attributes.entries[0]['link'] }})

      ![Image]({{ states.sensor.bon_appetit.attributes.entries[0]['image'] }})

      {{ states.sensor.bon_appetit.attributes.entries[0]['summary'] }}

      ---

      ## [{{ states.sensor.bon_appetit.attributes.entries[1]['title'] }}]({{
      states.sensor.bon_appetit.attributes.entries[1]['link'] }})

      ![Image]({{ states.sensor.bon_appetit.attributes.entries[1]['image'] }})

      {{ states.sensor.bon_appetit.attributes.entries[1]['summary'] }}

      ---

      ## [{{ states.sensor.bon_appetit.attributes.entries[2]['title'] }}]({{
      states.sensor.bon_appetit.attributes.entries[2]['link'] }})

      ![Image]({{ states.sensor.bon_appetit.attributes.entries[2]['image'] }})

      {{ states.sensor.bon_appetit.attributes.entries[2]['summary'] }}

      ---


      ## [{{ states.sensor.bon_appetit.attributes.entries[3]['title'] }}]({{
      states.sensor.bon_appetit.attributes.entries[3]['link'] }})

      ![Image]({{ states.sensor.bon_appetit.attributes.entries[3]['image'] }})

      {{ states.sensor.bon_appetit.attributes.entries[3]['summary'] }}

      ---

      ## [{{ states.sensor.bon_appetit.attributes.entries[4]['title'] }}]({{
      states.sensor.bon_appetit.attributes.entries[4]['link'] }})

      ![Image]({{ states.sensor.bon_appetit.attributes.entries[4]['image'] }})

      {{ states.sensor.bon_appetit.attributes.entries[4]['summary'] }}
    title: Bon Appeti
2 Likes

Im having trouble getting the image to show. How can I configure the list-card to show the image. The RSS feed sensor gives me this:

Here is how I go the rss feed: https://www.op.se/alla.xml to work.

You’ll need to go back and review the previous post on editing the sensor.py file.

I changed the way I’m including new image recognition.

In the sensor.py file look for the section of code that generates the entryValue for ‘image’ .

                if 'image' in self._inclusions and 'image' not in entryValue.keys():
                    images = []
                    if 'summary' in entry.keys():
                        images = re.findall(r"<img.+?src=\"(.+?)\".+?>", entry['summary'])
                    if images:
                        entryValue['image'] = images[0]
                    else:
                        entryValue['image'] = "https://www.home-assistant.io/images/favicon-192x192-full.png"

Cut and past the following code below that section to create a new inclusion type (image_op_se).

                if 'image_op_se' in self._inclusions:
                    images = []
                    if 'links' in entry.keys():
                        images = re.findall("\'0\', \'href\': \'(.*?)\?fit", str(entry['links']))
                    if images:
                        entryValue['image'] = images[0]
                    else:
                        entryValue['image'] = "https://www.home-assistant.io/images/favicon-192x192-full.png"

The sections still ends with:

                self._entries.append(entryValue)

The completed section of sensor.py will look like the following:

                for key, value in entry.items():
                    if (self._inclusions and key not in self._inclusions) or ('parsed' in key) or (key in self._exclusions):
                        continue
                    if key in ['published', 'updated', 'created', 'expired']:
                        value = parser.parse(value).strftime(self._date_format)

                    entryValue[key] = value

                if 'image' in self._inclusions and 'image' not in entryValue.keys():
                    images = []
                    if 'summary' in entry.keys():
                        images = re.findall(r"<img.+?src=\"(.+?)\".+?>", entry['summary'])
                    if images:
                        entryValue['image'] = images[0]
                    else:
                        entryValue['image'] = "https://www.home-assistant.io/images/favicon-192x192-full.png"
                        
                if 'image_op_se' in self._inclusions:
                    images = []
                    if 'links' in entry.keys():
                        images = re.findall("\'0\', \'href\': \'(.*?)\?fit", str(entry['links']))
                    if images:
                        entryValue['image'] = images[0]
                    else:
                        entryValue['image'] = "https://www.home-assistant.io/images/favicon-192x192-full.png"

                self._entries.append(entryValue)

In your configuration.yaml file you need to add the sensor with the new -image_op_se, it will still return an -image value (not -image_op_se) so it will work with the custom:list-card.

  - platform: feedparser
    name: OP SE
    feed_url: 'https://www.op.se/alla.xml'
    date_format: '%b %d %H%M'
    inclusions:
      - title
      - link
      - published
      - summary
      - image_op_se

After checking the configuration and restarting the server, the Developer Tools : States should display all the items. Show below is just one.

  - title: Coronarelaterat dödsfall inom omsorgen – första pĂ„ över en mĂ„nad
    summary: >-
      En vĂ„rdtagare inom Östersunds kommuns vĂ„rd och omsorg har avlidit den
      senaste veckan. Det Àr...
    published: Jan 28 1300
    link: >-
      https://www.op.se/artikel/coronarelaterat-dodsfall-inom-omsorgen-forsta-pa-over-en-manad
    image: >-
      https://bildix.mmcloud.se/bildix/api/images/0225265b-c37b-4675-a7ed-541663b3312d.jpeg

The Manual Lovelace card looks something like:

type: 'custom:list-card'
entity: sensor.op_se
title: Top Stories
feed_attribute: entries
row_limit: 20
columns:
  - title: ''
    type: image
    field: image
  - title: ''
    field: title
    add_link: link
  - title: ''
    field: published

Hope that helps


2 Likes

Wow, thank you so much. You spared me a couple of grey hairs. :grinning:

Is anyone getting this error lately? It began after I blew up my python 3.8.7 VENV and moved to 3.9.1, also upgrading from HA v 2021.1.5 to 2021.2 in the process. Weirdly a whole bunch of other sensors become unavailable if feedparser is installed but removing it fixes the other sensors.

AttributeError: module 'base64' has no attribute 'decodestring'

Hi,

I’m trying this same method with other site and the output without inclusion from sensor looks like this from developer tool view in home assistant (for one topic
)

> Entries:
  >- links:
      - rel: alternate
        type: text/html
        href: 'https://www.is.fi/kotimaa/art-2000007828211.html'
      - type: image/jpeg
        length: '0'
        href: >-
          https://is.mediadelivery.fi/img/468/97fc0f8e4ca4467f9f0768b521ec10e0.jpg
        rel: enclosure
    link: 'https://www.is.fi/kotimaa/art-2000007828211.html'
    title: Suuren teollisuushallin katto romahti KruunupyyssÀ
    title_detail:
      type: text/plain
      language: null
      base: 'https://www.is.fi/rss/tuoreimmat.xml'
      value: Suuren teollisuushallin katto romahti KruunupyyssÀ
    summary: >-
      Tutkinta on vielÀ kesken, mutta romahduksen syy lienee katolle kertynyt
      lumi.
    summary_detail:
      type: text/html
      language: null
      base: 'https://www.is.fi/rss/tuoreimmat.xml'
      value: >-
        Tutkinta on vielÀ kesken, mutta romahduksen syy lienee katolle kertynyt
        lumi.
    media_content:
      - url: >-
          https://is.mediadelivery.fi/img/468/97fc0f8e4ca4467f9f0768b521ec10e0.jpg
        type: image/jpeg
        width: '468'
    tags:
      - term: Kotimaa
        scheme: null
        label: null
    published: 'Fri, Feb 26 12:46 PM'
    id: is-2000007828211
    guidislink: false

However i cannot get the image to show up when i use the inclusions like in ur example:

 - platform: feedparser
    name: Engineering Feed
    feed_url: 'https://www.is.fi/rss/tuoreimmat.xml'
    inclusions:
      - title
      - link
      - published
      - summary
      - image_op_se

in lovelace card i have the same settings as in ur example.
If I change the feed_url to the same as in example it works fine.

I think there is something that i need to edit in that sensor.py part that is added in ur example but I haven’t really figured what is the missing thing now?

Any help is welcome, thanks.