Aftonbladet RSS feed

I have tried getting a good looking rss feed via aftonbladet

Here is what I have ended up with after inspiration from various tips and tricks on this site.

first I had to edit the feedparser sensor.py file to be able to catch the img url in the enclosure tag.

# Check for the enclosure tag
                if "enclosures" in entry:
                    enclosure = entry["enclosures"][0]  # Assuming only one enclosure
                    entry_value["image"] = enclosure.get("url", "")

                if not entry_value.get("image"):
                    # If there's no enclosure, check for image in summary
                    images = []
                    if "summary" in entry:
                        images = re.findall(
                            r"<img.+?src=\"(.+?)\".+?>", entry["summary"]
                        )
                    if images:
                        entry_value["image"] = images[0]
                    else:
                        entry_value[
                            "image"
                        ] = "https://www.home-assistant.io/images/favicon-192x192-full.png"

to create the sensor.aftonbladet, I have this in my sensors.yaml (moved my sensors to a seperate file instead of the configuration.yaml)

# RSS feedparser
  - platform: feedparser
    name: aftonbladet
    feed_url: "https://rss.aftonbladet.se/rss2/small/pages/sections/senastenytt/"
    date_format: "%Y-%m-%d %H:%M:%S"
    local_time: true
    show_topn: 5
    inclusions:
      - title
      - published
      - summary
      - link
    exclusions:
      - id

and finally I use the custom:list-card.

type: custom:list-card
entity: sensor.aftonbladet3
feed_attribute: entries
title: ' '
row_limit: 3
columns:
  - title: ''
    field: image
    type: image
    width: 90px
    height: auto
  - title: Title
    field: title
  - title: Summary
    field: summary
    add_link: link
  - title: Published
    field: published
style: |
  ha-card {
    max-width: 1200px;
    margin-left: auto;
    margin-right: auto;
  }
  img {
    width: 500px;
    height: auto;


  }
  tbody {
    display: grid;
    grid-gap: 2em;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  }
  @media only screen and (max-width: 1200px) {
    tbody {
      grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
      grid-gap: 0.5em;
    }
  }
  table {
    width: 100%;
  }
  table * {
    box-sizing: border-box;
    font-family: sans-serif;
  }
  th {
    display: none;
  }
  tr {
    display: flex;
    flex-direction: column;
    position: relative;
    width: 100%;
    margin: 0;
    overflow: hidden;
    border-radius: 10px;
  }

  .summary img {
    width: 100%;
    height: auto;
    vertical-align: middle;
  }

  tr .title {
    position: absolute;
    text-align: left;
    top: 0px;
    padding: 35px 10px 10px 10px;
    height: 100%;
    width: 100%;
    margin-left: auto;
    margin-right: auto;
    font-size: 20px;
    line-height: 24px;
    font-weight: bold;
    color: #fff;
    background-image: linear-gradient(rgba(0,0,0, 1), rgba(0,0,0, 0.3), rgba(0,0,0, 1));
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
    opacity: 1;
    backdrop-filter: blur(0px);
  }

  tr .published {
    position: absolute;
    text-align: left;
    font-size: 14px;
    top: 0px;
    color: darkorange;
    padding: 10px 10px;
    opacity: 1;
  }

  tr .summary {
  position: absolute;
  bottom: 10px;
  padding: 0 10px 10px;
  width: 100%;
  font-size: 16px;
  font-weight: 400;
  color: #fff;
  z-index: 1000;
  opacity: 1;
  text-align: left;
  opacity: 1;
  }

  tr .summary p:last-of-type {
    display: none;
  }

Here’s another way to do it.

type: custom:list-card
entity: sensor.aftonbladet3
feed_attribute: entries
title: ' '
row_limit: 5
columns:
  - title: ''
    field: image
    type: image
    width: 90px
    height: auto
  - title: Title
    field: title
    add_link: link
  - title: Published
    field: published
card_mod:
  style: |
    ha-card {
      max-width: 100%;
      margin: 0 auto;
    }
    img {
      width: 600px;
      height: auto;
    }
    tbody {
      display: grid;
      margin: 0;
      padding: 0;
      grid-gap: 0.3em;
      grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
    }
    table {
      width: 100%;
    }
    table * {
      box-sizing: border-box;
      font-family: sans-serif;
    }
    th {
      display: none;
    }
    tr {
      display: flex;
      flex-direction: column;
      position: relative;
      width: 100%;
      height: 90px;
      margin: 0;
      overflow: hidden;
      border-radius: 10px;
      transform: translateY(-110px);
    }
    tr .title {
      position: absolute;
      text-align: left;
      top: 0px;
      padding: 35px 10px 10px 10px;
      height: 100%;
      width: 100%;
      margin-left: auto;
      margin-right: auto;
      font-size: 18px;
      line-height: 24px;
      font-weight: bold;
      color: #fff;
      background-image: linear-gradient(rgba(0,0,0, 1), rgba(0,0,0, 0.5), rgba(0,0,0, 1));
      border-top-left-radius: 5px;
      border-top-right-radius: 5px;
      opacity: 1;
      backdrop-filter: blur(0px);
    }

    tr .published {
      position: absolute;
      text-align: left;
      font-size: 14px;
      top: 0px;
      color: darkorange;
      padding: 10px 10px;
      opacity: 1;
    }

    tr .summary {
    position: absolute;
    bottom: 10px;
    padding: 0 10px 10px;
    width: 100%;
    font-size: 16px;
    font-weight: 400;
    color: #fff;
    z-index: 1000;
    opacity: 1;
    text-align: left;
    opacity: 1;
    }

    tr .summary p:last-of-type {
      display: none;
    }
2 Likes

@chrs , thanks for sharing your code. This was exactly, what I’m looking for and already build it for one of my feeds :grinning:. I have two feeds (“Süddeutsche” and “Auto News”). “Auto News” has provided the image only in enclosure, so the change in sensor.py made it to work. But, now the entity of my first feed “Süddeutsche” disappeared?! Do you have an idea? Here is my feedparser config:

# RSS Feeds
  - platform: feedparser
    name: SĂĽddeutsche
    feed_url: 'https://rss.sueddeutsche.de/alles/'
    date_format: "%d.%m.%Y %H:%M"
    local_time: true
    scan_interval:
      hours: 3
  - platform: feedparser
    name: Auto News
    feed_url: 'https://www.stern.de/feed/standard/auto/'
    date_format: "%d.%m.%Y %H:%M"
    local_time: true
    scan_interval:
      hours: 3

@chrs , I changed the code a bit and the entity of both feeds are available again. But, now the “Auto News” feed cannot identify the right image and the HA.png is set. Unfortunately, I’m not a developer. Do you have an idea?

					# Check for the enclosure tag
                if "enclosures" in entry.keys():
                    enclosure = entry["enclosures"][0]  # Assuming only one enclosure
                    entry_value["image"] = enclosure.get("url", "")

                if not entry_value.get("image"):
                    # If there's no enclosure, check for image in summary
                    images = []
                    if "summary" in entry.keys():
                        images = re.findall(
                            r"<img.+?src=\"(.+?)\".+?>", entry["summary"]
                        )
                    if images:
                        entry_value["image"] = images[0]
                    else:
                        entry_value[
                            "image"
                        ] = "https://www.home-assistant.io/images/favicon-192x192-full.png"

looks correct, but im not a developer either. just a happy amateur.
my bet is that you need a fresh restart of the HA. if it doesnt work, try a private browser window. My feed gets weird sometimes also, but then I just reload my browser window.

feedparser have issues with collecting images, making my solution not working.

but if you change the sensor.py to this, it will start work again.


"""Feedparser sensor"""
from __future__ import annotations

import asyncio
import re
from datetime import timedelta

import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from dateutil import parser
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import CONF_NAME, CONF_SCAN_INTERVAL
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
import homeassistant.util.dt as dt

import feedparser

__version__ = "0.1.12"

COMPONENT_REPO = "https://github.com/custom-components/sensor.feedparser/"

REQUIREMENTS = ["feedparser"]

CONF_FEED_URL = "feed_url"
CONF_DATE_FORMAT = "date_format"
CONF_LOCAL_TIME  = "local_time"
CONF_INCLUSIONS = "inclusions"
CONF_EXCLUSIONS = "exclusions"
CONF_SHOW_TOPN = "show_topn"

DEFAULT_SCAN_INTERVAL = timedelta(hours=1)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Required(CONF_NAME): cv.string,
        vol.Required(CONF_FEED_URL): cv.string,
        vol.Required(CONF_DATE_FORMAT, default="%a, %b %d %I:%M %p"): cv.string,
        vol.Optional(CONF_LOCAL_TIME, default=False): cv.boolean,
        vol.Optional(CONF_SHOW_TOPN, default=9999): cv.positive_int,
        vol.Optional(CONF_INCLUSIONS, default=[]): vol.All(cv.ensure_list, [cv.string]),
        vol.Optional(CONF_EXCLUSIONS, default=[]): vol.All(cv.ensure_list, [cv.string]),
        vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL): cv.time_period,
    }
)


"""@asyncio.coroutine"""
async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_devices: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    async_add_devices(
        [
            FeedParserSensor(
                feed=config[CONF_FEED_URL],
                name=config[CONF_NAME],
                date_format=config[CONF_DATE_FORMAT],
                local_time=config[CONF_LOCAL_TIME],
                show_topn=config[CONF_SHOW_TOPN],
                inclusions=config[CONF_INCLUSIONS],
                exclusions=config[CONF_EXCLUSIONS],
                scan_interval=config[CONF_SCAN_INTERVAL],
            )
        ],
        True,
    )


class FeedParserSensor(SensorEntity):
    def __init__(
        self,
        feed: str,
        name: str,
        date_format: str,
        local_time: bool,
        show_topn: str,
        exclusions: str,
        inclusions: str,
        scan_interval: int,
    ) -> None:
        self._feed = feed
        self._attr_name = name
        self._attr_icon = "mdi:rss"
        self._date_format = date_format
        self._show_topn = show_topn
        self._local_time = local_time
        self._inclusions = inclusions
        self._exclusions = exclusions
        self._scan_interval = scan_interval
        self._attr_state = None
        self._entries = []
        self._attr_extra_state_attributes = {"entries": self._entries}

    def update(self):
        parsed_feed = feedparser.parse(self._feed)

        if not parsed_feed:
            return False
        else:
            self._attr_state = (
                self._show_topn
                if len(parsed_feed.entries) > self._show_topn
                else len(parsed_feed.entries)
            )
            self._entries = []

            for entry in parsed_feed.entries[: self._attr_state]:
                entry_value = {}

                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)
                        if self._local_time:
                            value = dt.as_local(value)
                        value = value.strftime(self._date_format)

                    entry_value[key] = value

                if "image" not in entry_value:
                    if "enclosures" in entry and entry["enclosures"]:
                        for enclosure in entry["enclosures"]:
                            if "url" in enclosure:
                                entry_value["image"] = enclosure["url"]
                                break

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

                self._entries.append(entry_value)

    @property
    def state(self):
        """Return the state of the sensor."""
        return self._attr_state

    @property
    def extra_state_attributes(self):
        return {"entries": self._entries}
1 Like