1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-20 02:48:57 +00:00

Add issue sensors to Overseerr integration (#158888)

This commit is contained in:
Anthony Garera
2025-12-16 11:11:28 -05:00
committed by GitHub
parent 7e141533bb
commit 3f57b46756
14 changed files with 515 additions and 39 deletions

View File

@@ -159,7 +159,8 @@ class OverseerrWebhookManager:
"""Handle webhook."""
data = await request.json()
LOGGER.debug("Received webhook payload: %s", data)
if data["notification_type"].startswith("MEDIA"):
notification_type = data["notification_type"]
if notification_type.startswith(("REQUEST_", "ISSUE_", "MEDIA_")):
await self.entry.runtime_data.async_refresh()
async_dispatcher_send(hass, EVENT_KEY, data)
return HomeAssistantView.json({"message": "ok"})

View File

@@ -22,6 +22,10 @@ REGISTERED_NOTIFICATIONS = (
| NotificationType.REQUEST_AVAILABLE
| NotificationType.REQUEST_PROCESSING_FAILED
| NotificationType.REQUEST_AUTOMATICALLY_APPROVED
| NotificationType.ISSUE_REPORTED
| NotificationType.ISSUE_COMMENTED
| NotificationType.ISSUE_RESOLVED
| NotificationType.ISSUE_REOPENED
)
JSON_PAYLOAD = (
'"{\\"notification_type\\":\\"{{notification_type}}\\",\\"subject\\":\\"{{subject}'

View File

@@ -6,7 +6,6 @@ from python_overseerr import (
OverseerrAuthenticationError,
OverseerrClient,
OverseerrConnectionError,
RequestCount,
)
from yarl import URL
@@ -18,11 +17,12 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN, LOGGER
from .models import OverseerrData
type OverseerrConfigEntry = ConfigEntry[OverseerrCoordinator]
class OverseerrCoordinator(DataUpdateCoordinator[RequestCount]):
class OverseerrCoordinator(DataUpdateCoordinator[OverseerrData]):
"""Class to manage fetching Overseerr data."""
config_entry: OverseerrConfigEntry
@@ -49,10 +49,12 @@ class OverseerrCoordinator(DataUpdateCoordinator[RequestCount]):
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) -> OverseerrData:
"""Fetch data from API endpoint."""
try:
return await self.client.get_request_count()
requests = await self.client.get_request_count()
issues = await self.client.get_issue_count()
return OverseerrData(requests=requests, issues=issues)
except OverseerrAuthenticationError as err:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,

View File

@@ -0,0 +1,13 @@
"""Data models for Overseerr integration."""
from dataclasses import dataclass
from python_overseerr import IssueCount, RequestCount
@dataclass
class OverseerrData:
"""Data model for Overseerr coordinator."""
requests: RequestCount
issues: IssueCount

View File

@@ -3,8 +3,6 @@
from collections.abc import Callable
from dataclasses import dataclass
from python_overseerr import RequestCount
from homeassistant.components.sensor import (
SensorEntity,
SensorEntityDescription,
@@ -16,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .const import REQUESTS
from .coordinator import OverseerrConfigEntry, OverseerrCoordinator
from .entity import OverseerrEntity
from .models import OverseerrData
PARALLEL_UPDATES = 0
@@ -24,7 +23,7 @@ PARALLEL_UPDATES = 0
class OverseerrSensorEntityDescription(SensorEntityDescription):
"""Describes Overseerr config sensor entity."""
value_fn: Callable[[RequestCount], int]
value_fn: Callable[[OverseerrData], int]
SENSORS: tuple[OverseerrSensorEntityDescription, ...] = (
@@ -32,43 +31,73 @@ SENSORS: tuple[OverseerrSensorEntityDescription, ...] = (
key="total_requests",
native_unit_of_measurement=REQUESTS,
state_class=SensorStateClass.TOTAL,
value_fn=lambda count: count.total,
value_fn=lambda data: data.requests.total,
),
OverseerrSensorEntityDescription(
key="movie_requests",
native_unit_of_measurement=REQUESTS,
state_class=SensorStateClass.TOTAL,
value_fn=lambda count: count.movie,
value_fn=lambda data: data.requests.movie,
),
OverseerrSensorEntityDescription(
key="tv_requests",
native_unit_of_measurement=REQUESTS,
state_class=SensorStateClass.TOTAL,
value_fn=lambda count: count.tv,
value_fn=lambda data: data.requests.tv,
),
OverseerrSensorEntityDescription(
key="pending_requests",
native_unit_of_measurement=REQUESTS,
state_class=SensorStateClass.TOTAL,
value_fn=lambda count: count.pending,
value_fn=lambda data: data.requests.pending,
),
OverseerrSensorEntityDescription(
key="declined_requests",
native_unit_of_measurement=REQUESTS,
state_class=SensorStateClass.TOTAL,
value_fn=lambda count: count.declined,
value_fn=lambda data: data.requests.declined,
),
OverseerrSensorEntityDescription(
key="processing_requests",
native_unit_of_measurement=REQUESTS,
state_class=SensorStateClass.TOTAL,
value_fn=lambda count: count.processing,
value_fn=lambda data: data.requests.processing,
),
OverseerrSensorEntityDescription(
key="available_requests",
native_unit_of_measurement=REQUESTS,
state_class=SensorStateClass.TOTAL,
value_fn=lambda count: count.available,
value_fn=lambda data: data.requests.available,
),
OverseerrSensorEntityDescription(
key="total_issues",
state_class=SensorStateClass.TOTAL,
value_fn=lambda data: data.issues.total,
),
OverseerrSensorEntityDescription(
key="open_issues",
state_class=SensorStateClass.TOTAL,
value_fn=lambda data: data.issues.open,
),
OverseerrSensorEntityDescription(
key="closed_issues",
state_class=SensorStateClass.TOTAL,
value_fn=lambda data: data.issues.closed,
),
OverseerrSensorEntityDescription(
key="video_issues",
state_class=SensorStateClass.TOTAL,
value_fn=lambda data: data.issues.video,
),
OverseerrSensorEntityDescription(
key="audio_issues",
state_class=SensorStateClass.TOTAL,
value_fn=lambda data: data.issues.audio,
),
OverseerrSensorEntityDescription(
key="subtitle_issues",
state_class=SensorStateClass.TOTAL,
value_fn=lambda data: data.issues.subtitles,
),
)

View File

@@ -50,26 +50,62 @@
}
},
"sensor": {
"audio_issues": {
"name": "Audio issues",
"state": {
"measurement": "issues"
}
},
"available_requests": {
"name": "Available requests"
},
"closed_issues": {
"name": "Closed issues",
"state": {
"measurement": "issues"
}
},
"declined_requests": {
"name": "Declined requests"
},
"movie_requests": {
"name": "Movie requests"
},
"open_issues": {
"name": "Open issues",
"state": {
"measurement": "issues"
}
},
"pending_requests": {
"name": "Pending requests"
},
"processing_requests": {
"name": "Processing requests"
},
"subtitle_issues": {
"name": "Subtitle issues",
"state": {
"measurement": "issues"
}
},
"total_issues": {
"name": "Total issues",
"state": {
"measurement": "issues"
}
},
"total_requests": {
"name": "Total requests"
},
"tv_requests": {
"name": "TV requests"
},
"video_issues": {
"name": "Video issues",
"state": {
"measurement": "issues"
}
}
}
},

View File

@@ -4,7 +4,7 @@ from collections.abc import Generator
from unittest.mock import AsyncMock, patch
import pytest
from python_overseerr import MovieDetails, RequestCount, RequestResponse
from python_overseerr import IssueCount, MovieDetails, RequestCount, RequestResponse
from python_overseerr.models import TVDetails, WebhookNotificationConfig
from homeassistant.components.overseerr import CONF_CLOUDHOOK_URL
@@ -49,6 +49,9 @@ def mock_overseerr_client() -> Generator[AsyncMock]:
client.get_request_count.return_value = RequestCount.from_json(
load_fixture("request_count.json", DOMAIN)
)
client.get_issue_count.return_value = IssueCount.from_json(
load_fixture("issue_count.json", DOMAIN)
)
client.get_webhook_notification_config.return_value = (
WebhookNotificationConfig.from_json(
load_fixture("webhook_config.json", DOMAIN)

View File

@@ -0,0 +1,9 @@
{
"total": 15,
"video": 6,
"audio": 4,
"subtitles": 3,
"others": 2,
"open": 10,
"closed": 5
}

View File

@@ -1,6 +1,6 @@
{
"enabled": true,
"types": 222,
"types": 4062,
"options": {
"jsonPayload": "{\"notification_type\":\"{{notification_type}}\",\"subject\":\"{{subject}}\",\"message\":\"{{message}}\",\"image\":\"{{image}}\",\"{{media}}\":{\"media_type\":\"{{media_type}}\",\"tmdb_id\":\"{{media_tmdbid}}\",\"tvdb_id\":\"{{media_tvdbid}}\",\"status\":\"{{media_status}}\",\"status4k\":\"{{media_status4k}}\"},\"{{request}}\":{\"request_id\":\"{{request_id}}\",\"requested_by_email\":\"{{requestedBy_email}}\",\"requested_by_username\":\"{{requestedBy_username}}\",\"requested_by_avatar\":\"{{requestedBy_avatar}}\",\"requested_by_settings_discord_id\":\"{{requestedBy_settings_discordId}}\",\"requested_by_settings_telegram_chat_id\":\"{{requestedBy_settings_telegramChatId}}\"},\"{{issue}}\":{\"issue_id\":\"{{issue_id}}\",\"issue_type\":\"{{issue_type}}\",\"issue_status\":\"{{issue_status}}\",\"reported_by_email\":\"{{reportedBy_email}}\",\"reported_by_username\":\"{{reportedBy_username}}\",\"reported_by_avatar\":\"{{reportedBy_avatar}}\",\"reported_by_settings_discord_id\":\"{{reportedBy_settings_discordId}}\",\"reported_by_settings_telegram_chat_id\":\"{{reportedBy_settings_telegramChatId}}\"},\"{{comment}}\":{\"comment_message\":\"{{comment_message}}\",\"commented_by_email\":\"{{commentedBy_email}}\",\"commented_by_username\":\"{{commentedBy_username}}\",\"commented_by_avatar\":\"{{commentedBy_avatar}}\",\"commented_by_settings_discord_id\":\"{{commentedBy_settings_discordId}}\",\"commented_by_settings_telegram_chat_id\":\"{{commentedBy_settings_telegramChatId}}\"}}",
"webhookUrl": "http://10.10.10.10:8123/api/webhook/test-webhook-id"

View File

@@ -0,0 +1,23 @@
{
"notification_type": "ISSUE_REPORTED",
"subject": "New Issue Reported",
"message": "A new video issue has been reported for Interstellar",
"image": "https://image.tmdb.org/t/p/w600_and_h900_bestv2/gEU2QniE6E77NI6lCU6MxlNBvIx.jpg",
"media": {
"media_type": "movie",
"tmdb_id": "157336",
"tvdb_id": "",
"status": "available",
"status4k": "unknown"
},
"issue": {
"issue_id": "1",
"issue_type": "video",
"issue_status": "open",
"reported_by_email": "user@example.com",
"reported_by_username": "testuser",
"reported_by_avatar": "/os_logo_square.png",
"reported_by_settings_discord_id": "",
"reported_by_settings_telegram_chat_id": ""
}
}

View File

@@ -2,6 +2,16 @@
# name: test_diagnostics_polling_instance
dict({
'coordinator_data': dict({
'issues': dict({
'audio': 4,
'closed': 5,
'open': 10,
'others': 2,
'subtitles': 3,
'total': 15,
'video': 6,
}),
'requests': dict({
'approved': 11,
'available': 8,
'declined': 0,
@@ -11,12 +21,23 @@
'total': 11,
'tv': 2,
}),
}),
'has_cloudhooks': False,
})
# ---
# name: test_diagnostics_webhook_instance
dict({
'coordinator_data': dict({
'issues': dict({
'audio': 4,
'closed': 5,
'open': 10,
'others': 2,
'subtitles': 3,
'total': 15,
'video': 6,
}),
'requests': dict({
'approved': 11,
'available': 8,
'declined': 0,
@@ -26,6 +47,7 @@
'total': 11,
'tv': 2,
}),
}),
'has_cloudhooks': True,
})
# ---

View File

@@ -1,4 +1,55 @@
# serializer version: 1
# name: test_all_entities[sensor.overseerr_audio_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.overseerr_audio_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Audio issues',
'platform': 'overseerr',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'audio_issues',
'unique_id': '01JG00V55WEVTJ0CJHM0GAD7PC-audio_issues',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.overseerr_audio_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Overseerr Audio issues',
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'context': <ANY>,
'entity_id': 'sensor.overseerr_audio_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '4',
})
# ---
# name: test_all_entities[sensor.overseerr_available_requests-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@@ -51,6 +102,57 @@
'state': '8',
})
# ---
# name: test_all_entities[sensor.overseerr_closed_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.overseerr_closed_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Closed issues',
'platform': 'overseerr',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'closed_issues',
'unique_id': '01JG00V55WEVTJ0CJHM0GAD7PC-closed_issues',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.overseerr_closed_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Overseerr Closed issues',
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'context': <ANY>,
'entity_id': 'sensor.overseerr_closed_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '5',
})
# ---
# name: test_all_entities[sensor.overseerr_declined_requests-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@@ -155,6 +257,57 @@
'state': '9',
})
# ---
# name: test_all_entities[sensor.overseerr_open_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.overseerr_open_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Open issues',
'platform': 'overseerr',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'open_issues',
'unique_id': '01JG00V55WEVTJ0CJHM0GAD7PC-open_issues',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.overseerr_open_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Overseerr Open issues',
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'context': <ANY>,
'entity_id': 'sensor.overseerr_open_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '10',
})
# ---
# name: test_all_entities[sensor.overseerr_pending_requests-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@@ -259,6 +412,108 @@
'state': '3',
})
# ---
# name: test_all_entities[sensor.overseerr_subtitle_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.overseerr_subtitle_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Subtitle issues',
'platform': 'overseerr',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'subtitle_issues',
'unique_id': '01JG00V55WEVTJ0CJHM0GAD7PC-subtitle_issues',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.overseerr_subtitle_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Overseerr Subtitle issues',
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'context': <ANY>,
'entity_id': 'sensor.overseerr_subtitle_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '3',
})
# ---
# name: test_all_entities[sensor.overseerr_total_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.overseerr_total_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Total issues',
'platform': 'overseerr',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'total_issues',
'unique_id': '01JG00V55WEVTJ0CJHM0GAD7PC-total_issues',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.overseerr_total_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Overseerr Total issues',
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'context': <ANY>,
'entity_id': 'sensor.overseerr_total_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '15',
})
# ---
# name: test_all_entities[sensor.overseerr_total_requests-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
@@ -363,3 +618,54 @@
'state': '2',
})
# ---
# name: test_all_entities[sensor.overseerr_video_issues-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'sensor',
'entity_category': None,
'entity_id': 'sensor.overseerr_video_issues',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Video issues',
'platform': 'overseerr',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'video_issues',
'unique_id': '01JG00V55WEVTJ0CJHM0GAD7PC-video_issues',
'unit_of_measurement': None,
})
# ---
# name: test_all_entities[sensor.overseerr_video_issues-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Overseerr Video issues',
'state_class': <SensorStateClass.TOTAL: 'total'>,
}),
'context': <ANY>,
'entity_id': 'sensor.overseerr_video_issues',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '6',
})
# ---

View File

@@ -72,7 +72,7 @@ async def test_proper_webhook_configuration(
"""Test the webhook configuration."""
await setup_integration(hass, mock_config_entry)
assert REGISTERED_NOTIFICATIONS == 222
assert REGISTERED_NOTIFICATIONS == 4062
mock_overseerr_client.test_webhook_notification_config.assert_not_called()
mock_overseerr_client.set_webhook_notification_config.assert_not_called()
@@ -83,7 +83,6 @@ async def test_proper_webhook_configuration(
[
{"return_value.enabled": False},
{"return_value.types": 4},
{"return_value.types": 4062},
{
"return_value.options": WebhookNotificationOptions(
webhook_url="http://example.com", json_payload=JSON_PAYLOAD
@@ -99,7 +98,6 @@ async def test_proper_webhook_configuration(
ids=[
"Disabled",
"Smaller scope",
"Bigger scope",
"Webhook URL",
"JSON Payload",
],
@@ -124,7 +122,6 @@ async def test_webhook_configuration_need_update(
[
{"return_value.enabled": False},
{"return_value.types": 4},
{"return_value.types": 4062},
{
"return_value.options": WebhookNotificationOptions(
webhook_url="http://example.com", json_payload=JSON_PAYLOAD
@@ -140,7 +137,6 @@ async def test_webhook_configuration_need_update(
ids=[
"Disabled",
"Smaller scope",
"Bigger scope",
"Webhook URL",
"JSON Payload",
],

View File

@@ -39,7 +39,7 @@ async def test_webhook_trigger_update(
mock_config_entry: MockConfigEntry,
hass_client_no_auth: ClientSessionGenerator,
) -> None:
"""Test all entities."""
"""Test webhook triggers coordinator update for request sensors."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("sensor.overseerr_available_requests").state == "8"
@@ -57,3 +57,35 @@ async def test_webhook_trigger_update(
await hass.async_block_till_done()
assert hass.states.get("sensor.overseerr_available_requests").state == "7"
async def test_webhook_issue_trigger_update(
hass: HomeAssistant,
mock_overseerr_client: AsyncMock,
mock_config_entry: MockConfigEntry,
hass_client_no_auth: ClientSessionGenerator,
) -> None:
"""Test webhook triggers coordinator update for issue sensors."""
await setup_integration(hass, mock_config_entry)
assert hass.states.get("sensor.overseerr_total_issues").state == "15"
assert hass.states.get("sensor.overseerr_open_issues").state == "10"
assert hass.states.get("sensor.overseerr_video_issues").state == "6"
mock_overseerr_client.get_issue_count.return_value.total = 16
mock_overseerr_client.get_issue_count.return_value.open = 11
mock_overseerr_client.get_issue_count.return_value.video = 7
client = await hass_client_no_auth()
await call_webhook(
hass,
await async_load_json_object_fixture(
hass, "webhook_issue_reported.json", DOMAIN
),
client,
)
await hass.async_block_till_done()
assert hass.states.get("sensor.overseerr_total_issues").state == "16"
assert hass.states.get("sensor.overseerr_open_issues").state == "11"
assert hass.states.get("sensor.overseerr_video_issues").state == "7"