Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow fetching Reddit user posts #124142

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 58 additions & 33 deletions homeassistant/components/reddit/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from datetime import timedelta
import logging
from typing import Any, Final

import praw
import voluptuous as vol
Expand All @@ -29,15 +30,17 @@

CONF_SORT_BY = "sort_by"
CONF_SUBREDDITS = "subreddits"

ATTR_BODY = "body"
ATTR_COMMENTS_NUMBER = "comms_num"
ATTR_CREATED = "created"
ATTR_POSTS = "posts"
ATTR_SUBREDDIT = "subreddit"
ATTR_SCORE = "score"
ATTR_TITLE = "title"
ATTR_URL = "url"
CONF_REDDITORS = "redditors"

ATTR_BODY: Final = "body"
ATTR_COMMENTS_NUMBER: Final = "comms_num"
ATTR_CREATED: Final = "created"
ATTR_POSTS: Final = "posts"
ATTR_SUBREDDIT: Final = "subreddit"
ATTR_SCORE: Final = "score"
ATTR_TITLE: Final = "title"
ATTR_URL: Final = "url"
ATTR_REDDITOR: Final = "redditor"

DEFAULT_NAME = "Reddit"

Expand All @@ -54,6 +57,7 @@
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_SUBREDDITS): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_REDDITORS): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_SORT_BY, default="hot"): vol.All(
cv.string, vol.In(LIST_TYPES)
),
Expand All @@ -70,10 +74,10 @@ def setup_platform(
) -> None:
"""Set up the Reddit sensor platform."""
subreddits = config[CONF_SUBREDDITS]
redditors = config.get(CONF_REDDITORS, [])
user_agent = f"{config[CONF_USERNAME]}_home_assistant_sensor"
limit = config[CONF_MAXIMUM]
sort_by = config[CONF_SORT_BY]

try:
reddit = praw.Reddit(
client_id=config[CONF_CLIENT_ID],
Expand All @@ -90,35 +94,53 @@ def setup_platform(
return

sensors = [
RedditSensor(reddit, subreddit, limit, sort_by) for subreddit in subreddits
RedditSensor(reddit, subreddit, limit, sort_by, ATTR_SUBREDDIT)
for subreddit in subreddits
]
sensors.extend(
RedditSensor(reddit, redditor, limit, sort_by, ATTR_REDDITOR)
for redditor in redditors
)

add_entities(sensors, True)


class RedditSensor(SensorEntity):
"""Representation of a Reddit sensor."""

def __init__(self, reddit, subreddit: str, limit: int, sort_by: str) -> None:
def __init__(
self,
reddit,
subreddit: str,
limit: int,
sort_by: str,
mode: str,
) -> None:
"""Initialize the Reddit sensor."""
self._reddit = reddit
self._subreddit = subreddit
self._limit = limit
self._sort_by = sort_by
self._mode = mode

self._subreddit_data: list = []

@property
def name(self):
def name(self) -> str:
"""Return the name of the sensor."""
return f"reddit_{self._subreddit}"
return (
self._mode == ATTR_SUBREDDIT
and f"reddit_{self._subreddit}"
or f"reddit_user_{self._subreddit}"
)

@property
def native_value(self):
def native_value(self) -> int:
"""Return the state of the sensor."""
return len(self._subreddit_data)

@property
def extra_state_attributes(self):
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
return {
ATTR_SUBREDDIT: self._subreddit,
Expand All @@ -127,7 +149,7 @@ def extra_state_attributes(self):
}

@property
def icon(self):
def icon(self) -> str:
"""Return the icon to use in the frontend."""
return "mdi:reddit"

Expand All @@ -136,22 +158,25 @@ def update(self) -> None:
self._subreddit_data = []

try:
subreddit = self._reddit.subreddit(self._subreddit)
if hasattr(subreddit, self._sort_by):
method_to_call = getattr(subreddit, self._sort_by)

for submission in method_to_call(limit=self._limit):
self._subreddit_data.append(
{
ATTR_ID: submission.id,
ATTR_URL: submission.url,
ATTR_TITLE: submission.title,
ATTR_SCORE: submission.score,
ATTR_COMMENTS_NUMBER: submission.num_comments,
ATTR_CREATED: submission.created,
ATTR_BODY: submission.selftext,
}
)
if hasattr(self._reddit, self._mode):
data_source = getattr(self._reddit, self._mode)(self._subreddit)
if hasattr(data_source, self._sort_by):
method_to_call = getattr(data_source, self._sort_by)

for submission in method_to_call(limit=self._limit):
self._subreddit_data.append(
{
ATTR_ID: getattr(submission, "id", ""),
ATTR_URL: getattr(submission, "url", ""),
ATTR_TITLE: getattr(submission, "title", ""),
ATTR_SCORE: getattr(submission, "score", ""),
ATTR_COMMENTS_NUMBER: getattr(
submission, "num_comments", ""
),
ATTR_CREATED: getattr(submission, "created", ""),
ATTR_BODY: getattr(submission, "selftext", ""),
}
)

except praw.exceptions.PRAWException as err:
_LOGGER.error("Reddit error %s", err)
23 changes: 23 additions & 0 deletions tests/components/reddit/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@
}
}

VALID_CONFIG_REDDITOR = {
"sensor": {
"platform": DOMAIN,
CONF_CLIENT_ID: "test_client_id",
CONF_CLIENT_SECRET: "test_client_secret",
CONF_USERNAME: "test_username",
CONF_PASSWORD: "test_password",
"subreddits": [],
"redditors": ["test_user"],
}
}

VALID_LIMITED_CONFIG = {
"sensor": {
"platform": DOMAIN,
Expand All @@ -58,6 +70,7 @@
CONF_USERNAME: "test_username",
CONF_PASSWORD: "test_password",
"subreddits": ["worldnews", "news"],
"redditors": [],
"sort_by": "invalid_sort_by",
}
}
Expand Down Expand Up @@ -177,6 +190,16 @@ async def test_setup_with_valid_config(hass: HomeAssistant) -> None:
assert state.attributes[CONF_SORT_BY] == "hot"


@patch("praw.Reddit", new=MockPraw)
async def test_setup_with_valid_config_redditor(hass: HomeAssistant) -> None:
"""Test the platform setup with Reddit configuration."""
assert await async_setup_component(hass, "sensor", VALID_CONFIG_REDDITOR)
await hass.async_block_till_done()

state = hass.states.get("sensor.reddit_user_test_user")
assert int(state.state) == 0


@patch("praw.Reddit", new=MockPraw)
async def test_setup_with_invalid_config(hass: HomeAssistant) -> None:
"""Test the platform setup with invalid Reddit configuration."""
Expand Down