Skip to content

Commit

Permalink
Merge pull request #124 from ogajduse/pre-release-0.2.0-preparations
Browse files Browse the repository at this point in the history
  • Loading branch information
ogajduse authored Feb 25, 2024
2 parents a632aed + 241be9c commit 2be3736
Show file tree
Hide file tree
Showing 33 changed files with 5,900 additions and 4,539 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ jobs:
matrix:
python-version: ["3.11"]
steps:
- name: Install xmllint
run: sudo apt -y install libxml2-utils

- name: Checkout Repository
uses: actions/checkout@v4

Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,11 @@
build/
*.egg-info/
custom_components/hacs
test_hass/.HA_VERSION
test_hass/.storage*
test_hass/blueprints/
test_hass/custom_components
test_hass/home-assistant_v2.db*
test_hass/home-assistant.log*
test_hass/sensors.yaml
test_hass/www/
24 changes: 17 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,22 @@ repos:
- id: end-of-file-fixer
- id: check-yaml
exclude: ^test_hass/configuration.yaml$
- id: check-toml
- id: debug-statements
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.0
- repo: https://github.com/lsst-ts/pre-commit-xmllint
rev: v1.0.0
hooks:
- id: pyupgrade
args: [--py311-plus]
- repo: https://github.com/psf/black
rev: "22.10.0"
- id: format-xmllint
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.12.0
hooks:
- id: black
- id: pretty-format-toml
args: ["--autofix", "--no-sort"]
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.16
hooks:
- id: validate-pyproject
additional_dependencies: ["validate-pyproject-schema-store[all]"]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.4.1"
hooks:
Expand All @@ -30,6 +36,10 @@ repos:
types-PyYAML,
types-requests,
]
- repo: https://github.com/psf/black
rev: "22.10.0"
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.0.278
Expand Down
20 changes: 20 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
// Example of attaching to local debug server
"name": "Python: Attach Local",
"type": "python",
"request": "attach",
"port": 5678,
"host": "localhost",
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
],
}
]
}
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
{
"python.formatting.provider": "black"
"python.formatting.provider": "black",
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
59 changes: 42 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,54 @@ Hey dude! Help me out for a couple of :beers: or a :coffee:!

[![coffee](https://www.buymeacoffee.com/assets/img/custom_images/black_img.png)](https://www.buymeacoffee.com/zJtVxUAgH)

To get started put `/custom_components/feedparser/` here:
`<config directory>/custom_components/feedparser/`

## Installation
[![hacs_badge](https://img.shields.io/badge/HACS-Custom-41BDF5.svg)](https://github.com/hacs/integration)

1. Open HACS Settings and add this repository (https://github.com/custom-components/feedparser/)
as a Custom Repository (use **Integration** as the category).
2. The `feedparser` page should automatically load (or find it in the HACS Store)
3. Click `Install`

Alternatively, click on the button below to add the repository:

[![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?category=Integration&repository=feedparser&owner=custom-components)


## Configuration

**Example configuration.yaml:**

```yaml
sensor:
platform: feedparser
name: Engineering Feed
feed_url: 'https://www.sciencedaily.com/rss/matter_energy/engineering.xml'
date_format: '%a, %d %b %Y %H:%M:%S %Z'
scan_interval:
hours: 3
inclusions:
- title
- link
- description
- image
- pubDate
exclusions:
- language
- platform: feedparser
name: Engineering Feed
feed_url: 'https://www.sciencedaily.com/rss/matter_energy/engineering.xml'
date_format: '%a, %d %b %Y %H:%M:%S %Z'
scan_interval:
hours: 3
inclusions:
- title
- link
- description
- image
- published
exclusions:
- language

# Configuration of the second sensor tracking a different RSS feed
- platform: feedparser
name: Algemeen
feed_url: https://www.nu.nl/rss/Algemeen
local_time: true
show_topn: 1
```
If you wish the integration to look for enclosures in the feed entries, add `image` to `inclusions` list. Do not use `enclosure`.
The integration tries to get the link to an image for the given feed item and stores it under the attribute named `image`. If it fails to find it, it assigns the Home Assistant logo to it instead.

Note that the original `pubDate` field is available under `published` attribute for the given feed entry. Other date-type values that can be available are `updated`, `created` and `expired`. Please refer to [the documentation of the original feedparser](https://feedparser.readthedocs.io/en/latest/date-parsing.html) library.

**Configuration variables:**

key | description
Expand Down Expand Up @@ -65,6 +90,6 @@ Due to how `custom_components` are loaded, it is normal to see a `ModuleNotFound
[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg?style=for-the-badge
[forum]: https://community.home-assistant.io/t/custom-component-rss-feed-parser/64637
[license-shield]: https://img.shields.io/github/license/custom-components/feedparser.svg?style=for-the-badge
[maintenance-shield]: https://img.shields.io/badge/maintainer-Ian%20Richardson%20%40iantrich-blue.svg?style=for-the-badge
[maintenance-shield]: https://img.shields.io/badge/maintainer-Ondrej%20Gajdusek%20%40ogajduse-blue.svg?style=for-the-badge
[releases-shield]: https://img.shields.io/github/release/custom-components/feedparser.svg?style=for-the-badge
[releases]: https://github.com/custom-components/feedparser/releases
2 changes: 1 addition & 1 deletion custom_components/feedparser/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"documentation": "https://github.com/custom-components/feedparser/blob/master/README.md",
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/custom-components/feedparser/issues",
"requirements": ["feedparser==6.0.10", "python-dateutil"],
"requirements": ["feedparser==6.0.11", "python-dateutil", "requests-file", "requests"],
"version": "0.1.11"
}
38 changes: 26 additions & 12 deletions custom_components/feedparser/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@
CONF_INCLUSIONS = "inclusions"
CONF_EXCLUSIONS = "exclusions"
CONF_SHOW_TOPN = "show_topn"
CONF_REMOVE_SUMMARY_IMG = "remove_summary_image"

DEFAULT_DATE_FORMAT = "%a, %b %d %I:%M %p"
DEFAULT_SCAN_INTERVAL = timedelta(hours=1)
DEFAULT_THUMBNAIL = "https://www.home-assistant.io/images/favicon-192x192-full.png"
DEFAULT_TOPN = 9999
USER_AGENT = f"Home Assistant Feed-parser Integration {__version__}"
IMAGE_REGEX = r"<img.+?src=\"(.+?)\".+?>"

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
Expand All @@ -49,6 +51,7 @@
vol.Required(CONF_DATE_FORMAT, default=DEFAULT_DATE_FORMAT): cv.string,
vol.Optional(CONF_LOCAL_TIME, default=False): cv.boolean,
vol.Optional(CONF_SHOW_TOPN, default=DEFAULT_TOPN): cv.positive_int,
vol.Optional(CONF_REMOVE_SUMMARY_IMG, default=False): cv.boolean,
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,
Expand All @@ -72,6 +75,7 @@ async def async_setup_platform(
name=config[CONF_NAME],
date_format=config[CONF_DATE_FORMAT],
show_topn=config[CONF_SHOW_TOPN],
remove_summary_image=config[CONF_REMOVE_SUMMARY_IMG],
inclusions=config[CONF_INCLUSIONS],
exclusions=config[CONF_EXCLUSIONS],
scan_interval=config[CONF_SCAN_INTERVAL],
Expand All @@ -95,6 +99,7 @@ def __init__(
name: str,
date_format: str,
show_topn: int,
remove_summary_image: bool,
exclusions: list[str | None],
inclusions: list[str | None],
scan_interval: timedelta,
Expand All @@ -106,6 +111,7 @@ def __init__(
self._attr_icon = "mdi:rss"
self._date_format = date_format
self._show_topn: int = show_topn
self._remove_summary_image = remove_summary_image
self._inclusions = inclusions
self._exclusions = exclusions
self._scan_interval = scan_interval
Expand All @@ -119,7 +125,9 @@ def __repr__(self: FeedParserSensor) -> str:
"""Return the representation."""
return (
f'FeedParserSensor(name="{self.name}", feed="{self._feed}", '
f"show_topn={self._show_topn}, inclusions={self._inclusions}, "
f"show_topn={self._show_topn}, "
f"remove_summary_image={self._remove_summary_image}, "
f"inclusions={self._inclusions}, "
f"exclusions={self._exclusions}, scan_interval={self._scan_interval}, "
f'local_time={self._local_time}, date_format="{self._date_format}")'
)
Expand Down Expand Up @@ -191,14 +199,20 @@ def _generate_sensor_entry(
else:
sensor_entry[key] = value

if "image" in self._inclusions and "image" not in sensor_entry:
sensor_entry["image"] = self._process_image(feed_entry)
if (
"link" in self._inclusions
and "link" not in sensor_entry
and (processed_link := self._process_link(feed_entry))
):
sensor_entry["link"] = processed_link
if "image" in self._inclusions and "image" not in sensor_entry:
sensor_entry["image"] = self._process_image(feed_entry)
if (
"link" in self._inclusions
and "link" not in sensor_entry
and (processed_link := self._process_link(feed_entry))
):
sensor_entry["link"] = processed_link
if self._remove_summary_image and "summary" in sensor_entry:
sensor_entry["summary"] = re.sub(
IMAGE_REGEX,
"",
sensor_entry["summary"],
)
_LOGGER.debug("Feed %s: Generated sensor entry: %s", self.name, sensor_entry)
return sensor_entry

Expand Down Expand Up @@ -239,7 +253,7 @@ def _parse_date(self: FeedParserSensor, date: str) -> datetime:
return parsed_time

def _process_image(self: FeedParserSensor, feed_entry: FeedParserDict) -> str:
if "enclosures" in feed_entry and feed_entry["enclosures"]:
if feed_entry.get("enclosures"):
images = [
enc for enc in feed_entry["enclosures"] if enc.type.startswith("image/")
]
Expand All @@ -248,7 +262,7 @@ def _process_image(self: FeedParserSensor, feed_entry: FeedParserDict) -> str:
return images[0]["href"]
elif "summary" in feed_entry:
images = re.findall(
r"<img.+?src=\"(.+?)\".+?>",
IMAGE_REGEX,
feed_entry["summary"],
)
if images:
Expand All @@ -265,7 +279,7 @@ def _process_link(self: FeedParserSensor, feed_entry: FeedParserDict) -> str:
"""Return link from feed entry."""
if "links" in feed_entry:
if len(feed_entry["links"]) > 1:
_LOGGER.warning(
_LOGGER.debug(
"Feed %s: More than one link found for %s. Using the first link.",
self.name,
feed_entry,
Expand Down
Loading

0 comments on commit 2be3736

Please sign in to comment.