Only add Overseerr event if we are push based (#136258)

This commit is contained in:
Joost Lekkerkerker 2025-01-22 19:58:48 +01:00 committed by GitHub
parent ad205aeea3
commit 9f2a6af1ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 120 additions and 9 deletions

View File

@ -116,15 +116,13 @@ class OverseerrWebhookManager:
allowed_methods=[METH_POST], allowed_methods=[METH_POST],
) )
if not await self.check_need_change(): if not await self.check_need_change():
self.entry.runtime_data.push = True
return return
for url in self.webhook_urls: for url in self.webhook_urls:
if await self.test_and_set_webhook(url): if await self.test_and_set_webhook(url):
return return
LOGGER.info("Failed to register Overseerr webhook") LOGGER.info("Failed to register Overseerr webhook")
if ( if cloud.async_active_subscription(self.hass):
cloud.async_active_subscription(self.hass)
and CONF_CLOUDHOOK_URL not in self.entry.data
):
LOGGER.info("Trying to register a cloudhook URL") LOGGER.info("Trying to register a cloudhook URL")
url = await _async_cloudhook_generate_url(self.hass, self.entry) url = await _async_cloudhook_generate_url(self.hass, self.entry)
if await self.test_and_set_webhook(url): if await self.test_and_set_webhook(url):
@ -151,6 +149,7 @@ class OverseerrWebhookManager:
webhook_url=url, webhook_url=url,
json_payload=JSON_PAYLOAD, json_payload=JSON_PAYLOAD,
) )
self.entry.runtime_data.push = True
return True return True
return False return False

View File

@ -47,6 +47,7 @@ class OverseerrCoordinator(DataUpdateCoordinator[RequestCount]):
session=async_get_clientsession(hass), session=async_get_clientsession(hass),
) )
self.url = URL.build(host=host, port=port, scheme="https" if ssl else "http") self.url = URL.build(host=host, port=port, scheme="https" if ssl else "http")
self.push = False
async def _async_update_data(self) -> RequestCount: async def _async_update_data(self) -> RequestCount:
"""Fetch data from API endpoint.""" """Fetch data from API endpoint."""

View File

@ -4,11 +4,13 @@ from dataclasses import dataclass
from typing import Any from typing import Any
from homeassistant.components.event import EventEntity, EventEntityDescription from homeassistant.components.event import EventEntity, EventEntityDescription
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import EVENT_KEY from . import DOMAIN, EVENT_KEY
from .coordinator import OverseerrConfigEntry, OverseerrCoordinator from .coordinator import OverseerrConfigEntry, OverseerrCoordinator
from .entity import OverseerrEntity from .entity import OverseerrEntity
@ -47,10 +49,17 @@ async def async_setup_entry(
"""Set up Overseerr sensor entities based on a config entry.""" """Set up Overseerr sensor entities based on a config entry."""
coordinator = entry.runtime_data coordinator = entry.runtime_data
async_add_entities( ent_reg = er.async_get(hass)
OverseerrEvent(coordinator, description) for description in EVENTS
event_entities_setup_before = ent_reg.async_get_entity_id(
Platform.EVENT, DOMAIN, f"{entry.entry_id}-media"
) )
if coordinator.push or event_entities_setup_before:
async_add_entities(
OverseerrEvent(coordinator, description) for description in EVENTS
)
class OverseerrEvent(OverseerrEntity, EventEntity): class OverseerrEvent(OverseerrEntity, EventEntity):
"""Defines a Overseerr event entity.""" """Defines a Overseerr event entity."""
@ -94,7 +103,7 @@ class OverseerrEvent(OverseerrEntity, EventEntity):
@property @property
def available(self) -> bool: def available(self) -> bool:
"""Return True if entity is available.""" """Return True if entity is available."""
return self._attr_available return self._attr_available and self.coordinator.push
def parse_event(event: dict[str, Any], nullable_fields: list[str]) -> dict[str, Any]: def parse_event(event: dict[str, Any], nullable_fields: list[str]) -> dict[str, Any]:

View File

@ -107,3 +107,64 @@ async def test_event_goes_unavailable(
assert ( assert (
hass.states.get("event.overseerr_last_media_event").state == STATE_UNAVAILABLE hass.states.get("event.overseerr_last_media_event").state == STATE_UNAVAILABLE
) )
async def test_not_push_based(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_overseerr_client_needs_change: AsyncMock,
) -> None:
"""Test event entities aren't created if not push based."""
mock_overseerr_client_needs_change.test_webhook_notification_config.return_value = (
False
)
await setup_integration(hass, mock_config_entry)
assert hass.states.get("event.overseerr_last_media_event") is None
async def test_cant_fetch_webhook_config(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_overseerr_client: AsyncMock,
) -> None:
"""Test event entities aren't created if not push based."""
mock_overseerr_client.get_webhook_notification_config.side_effect = (
OverseerrConnectionError("Boom")
)
await setup_integration(hass, mock_config_entry)
assert hass.states.get("event.overseerr_last_media_event") is None
async def test_not_push_based_but_was_before(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_overseerr_client_needs_change: AsyncMock,
entity_registry: er.EntityRegistry,
) -> None:
"""Test event entities are created if push based in the past."""
entity_registry.async_get_or_create(
Platform.EVENT,
DOMAIN,
f"{mock_config_entry.entry_id}-media",
suggested_object_id="overseerr_last_media_event",
disabled_by=None,
)
mock_overseerr_client_needs_change.test_webhook_notification_config.return_value = (
False
)
await setup_integration(hass, mock_config_entry)
assert hass.states.get("event.overseerr_last_media_event") is not None
assert (
hass.states.get("event.overseerr_last_media_event").state == STATE_UNAVAILABLE
)

View File

@ -9,6 +9,7 @@ from python_overseerr.models import WebhookNotificationOptions
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
from homeassistant.components import cloud from homeassistant.components import cloud
from homeassistant.components.cloud import CloudNotAvailable
from homeassistant.components.overseerr import ( from homeassistant.components.overseerr import (
CONF_CLOUDHOOK_URL, CONF_CLOUDHOOK_URL,
JSON_PAYLOAD, JSON_PAYLOAD,
@ -362,10 +363,50 @@ async def test_cloudhook_not_connecting(
len( len(
mock_overseerr_client_needs_change.test_webhook_notification_config.mock_calls mock_overseerr_client_needs_change.test_webhook_notification_config.mock_calls
) )
== 2 == 3
) )
mock_overseerr_client_needs_change.set_webhook_notification_config.assert_not_called() mock_overseerr_client_needs_change.set_webhook_notification_config.assert_not_called()
assert hass.config_entries.async_entries(DOMAIN) assert hass.config_entries.async_entries(DOMAIN)
fake_create_cloudhook.assert_not_called() fake_create_cloudhook.assert_not_called()
async def test_removing_entry_with_cloud_unavailable(
hass: HomeAssistant,
mock_cloudhook_config_entry: MockConfigEntry,
mock_overseerr_client: AsyncMock,
) -> None:
"""Test handling cloud unavailable when deleting entry."""
await mock_cloud(hass)
await hass.async_block_till_done()
with (
patch("homeassistant.components.cloud.async_is_logged_in", return_value=True),
patch("homeassistant.components.cloud.async_is_connected", return_value=True),
patch.object(cloud, "async_active_subscription", return_value=True),
patch(
"homeassistant.components.cloud.async_create_cloudhook",
return_value="https://hooks.nabu.casa/ABCD",
),
patch(
"homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation",
),
patch(
"homeassistant.components.cloud.async_delete_cloudhook",
side_effect=CloudNotAvailable(),
),
):
await setup_integration(hass, mock_cloudhook_config_entry)
assert cloud.async_active_subscription(hass) is True
await hass.async_block_till_done()
assert hass.config_entries.async_entries(DOMAIN)
for config_entry in hass.config_entries.async_entries(DOMAIN):
await hass.config_entries.async_remove(config_entry.entry_id)
await hass.async_block_till_done()
assert not hass.config_entries.async_entries(DOMAIN)