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:
@@ -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"})
|
||||
|
||||
@@ -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}'
|
||||
|
||||
@@ -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,
|
||||
|
||||
13
homeassistant/components/overseerr/models.py
Normal file
13
homeassistant/components/overseerr/models.py
Normal 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
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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)
|
||||
|
||||
9
tests/components/overseerr/fixtures/issue_count.json
Normal file
9
tests/components/overseerr/fixtures/issue_count.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"total": 15,
|
||||
"video": 6,
|
||||
"audio": 4,
|
||||
"subtitles": 3,
|
||||
"others": 2,
|
||||
"open": 10,
|
||||
"closed": 5
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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": ""
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,25 @@
|
||||
# name: test_diagnostics_polling_instance
|
||||
dict({
|
||||
'coordinator_data': dict({
|
||||
'approved': 11,
|
||||
'available': 8,
|
||||
'declined': 0,
|
||||
'movie': 9,
|
||||
'pending': 0,
|
||||
'processing': 3,
|
||||
'total': 11,
|
||||
'tv': 2,
|
||||
'issues': dict({
|
||||
'audio': 4,
|
||||
'closed': 5,
|
||||
'open': 10,
|
||||
'others': 2,
|
||||
'subtitles': 3,
|
||||
'total': 15,
|
||||
'video': 6,
|
||||
}),
|
||||
'requests': dict({
|
||||
'approved': 11,
|
||||
'available': 8,
|
||||
'declined': 0,
|
||||
'movie': 9,
|
||||
'pending': 0,
|
||||
'processing': 3,
|
||||
'total': 11,
|
||||
'tv': 2,
|
||||
}),
|
||||
}),
|
||||
'has_cloudhooks': False,
|
||||
})
|
||||
@@ -17,14 +28,25 @@
|
||||
# name: test_diagnostics_webhook_instance
|
||||
dict({
|
||||
'coordinator_data': dict({
|
||||
'approved': 11,
|
||||
'available': 8,
|
||||
'declined': 0,
|
||||
'movie': 9,
|
||||
'pending': 0,
|
||||
'processing': 3,
|
||||
'total': 11,
|
||||
'tv': 2,
|
||||
'issues': dict({
|
||||
'audio': 4,
|
||||
'closed': 5,
|
||||
'open': 10,
|
||||
'others': 2,
|
||||
'subtitles': 3,
|
||||
'total': 15,
|
||||
'video': 6,
|
||||
}),
|
||||
'requests': dict({
|
||||
'approved': 11,
|
||||
'available': 8,
|
||||
'declined': 0,
|
||||
'movie': 9,
|
||||
'pending': 0,
|
||||
'processing': 3,
|
||||
'total': 11,
|
||||
'tv': 2,
|
||||
}),
|
||||
}),
|
||||
'has_cloudhooks': True,
|
||||
})
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
# ---
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user