From 018197e41a5972fc6e0c21da633b7de7803e4696 Mon Sep 17 00:00:00 2001 From: Manu <4445816+tr4nt0r@users.noreply.github.com> Date: Sat, 2 Aug 2025 19:55:45 +0200 Subject: [PATCH] Add notifiers to send direct messages to friends in PlayStation Network (#149844) --- .../components/playstation_network/notify.py | 95 +++++++++++++++---- .../playstation_network/strings.json | 3 + .../snapshots/test_notify.ambr | 49 ++++++++++ .../playstation_network/test_notify.py | 11 ++- 4 files changed, 134 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/playstation_network/notify.py b/homeassistant/components/playstation_network/notify.py index 872ad98a594..a06359ebffc 100644 --- a/homeassistant/components/playstation_network/notify.py +++ b/homeassistant/components/playstation_network/notify.py @@ -3,6 +3,7 @@ from __future__ import annotations from enum import StrEnum +from typing import TYPE_CHECKING from psnawp_api.core.psnawp_exceptions import ( PSNAWPClientError, @@ -10,12 +11,14 @@ from psnawp_api.core.psnawp_exceptions import ( PSNAWPNotFoundError, PSNAWPServerError, ) +from psnawp_api.models.group.group import Group from homeassistant.components.notify import ( DOMAIN as NOTIFY_DOMAIN, NotifyEntity, NotifyEntityDescription, ) +from homeassistant.config_entries import ConfigSubentry from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er @@ -24,6 +27,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import DOMAIN from .coordinator import ( PlaystationNetworkConfigEntry, + PlaystationNetworkFriendDataCoordinator, PlaystationNetworkGroupsUpdateCoordinator, ) from .entity import PlaystationNetworkServiceEntity @@ -35,6 +39,7 @@ class PlaystationNetworkNotify(StrEnum): """PlayStation Network sensors.""" GROUP_MESSAGE = "group_message" + DIRECT_MESSAGE = "direct_message" async def async_setup_entry( @@ -45,6 +50,7 @@ async def async_setup_entry( """Set up the notify entity platform.""" coordinator = config_entry.runtime_data.groups + groups_added: set[str] = set() entity_registry = er.async_get(hass) @@ -72,8 +78,50 @@ async def async_setup_entry( coordinator.async_add_listener(add_entities) add_entities() + for subentry_id, friend_coordinator in config_entry.runtime_data.friends.items(): + async_add_entities( + [ + PlaystationNetworkDirectMessageNotifyEntity( + friend_coordinator, + config_entry.subentries[subentry_id], + ) + ], + config_subentry_id=subentry_id, + ) -class PlaystationNetworkNotifyEntity(PlaystationNetworkServiceEntity, NotifyEntity): + +class PlaystationNetworkNotifyBaseEntity(PlaystationNetworkServiceEntity, NotifyEntity): + """Base class of PlayStation Network notify entity.""" + + group: Group | None = None + + def send_message(self, message: str, title: str | None = None) -> None: + """Send a message.""" + if TYPE_CHECKING: + assert self.group + try: + self.group.send_message(message) + except PSNAWPNotFoundError as e: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="group_invalid", + translation_placeholders=dict(self.translation_placeholders), + ) from e + except PSNAWPForbiddenError as e: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="send_message_forbidden", + translation_placeholders=dict(self.translation_placeholders), + ) from e + except (PSNAWPServerError, PSNAWPClientError) as e: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="send_message_failed", + translation_placeholders=dict(self.translation_placeholders), + ) from e + + +class PlaystationNetworkNotifyEntity(PlaystationNetworkNotifyBaseEntity): """Representation of a PlayStation Network notify entity.""" coordinator: PlaystationNetworkGroupsUpdateCoordinator @@ -101,26 +149,31 @@ class PlaystationNetworkNotifyEntity(PlaystationNetworkServiceEntity, NotifyEnti super().__init__(coordinator, self.entity_description) + +class PlaystationNetworkDirectMessageNotifyEntity(PlaystationNetworkNotifyBaseEntity): + """Representation of a PlayStation Network notify entity for sending direct messages.""" + + coordinator: PlaystationNetworkFriendDataCoordinator + + def __init__( + self, + coordinator: PlaystationNetworkFriendDataCoordinator, + subentry: ConfigSubentry, + ) -> None: + """Initialize a notification entity.""" + + self.entity_description = NotifyEntityDescription( + key=PlaystationNetworkNotify.DIRECT_MESSAGE, + translation_key=PlaystationNetworkNotify.DIRECT_MESSAGE, + ) + + super().__init__(coordinator, self.entity_description, subentry) + def send_message(self, message: str, title: str | None = None) -> None: """Send a message.""" - try: - self.group.send_message(message) - except PSNAWPNotFoundError as e: - raise HomeAssistantError( - translation_domain=DOMAIN, - translation_key="group_invalid", - translation_placeholders=dict(self.translation_placeholders), - ) from e - except PSNAWPForbiddenError as e: - raise HomeAssistantError( - translation_domain=DOMAIN, - translation_key="send_message_forbidden", - translation_placeholders=dict(self.translation_placeholders), - ) from e - except (PSNAWPServerError, PSNAWPClientError) as e: - raise HomeAssistantError( - translation_domain=DOMAIN, - translation_key="send_message_failed", - translation_placeholders=dict(self.translation_placeholders), - ) from e + if not self.group: + self.group = self.coordinator.psn.psn.group( + users_list=[self.coordinator.user] + ) + super().send_message(message, title) diff --git a/homeassistant/components/playstation_network/strings.json b/homeassistant/components/playstation_network/strings.json index e5192f42873..26a1b336e2d 100644 --- a/homeassistant/components/playstation_network/strings.json +++ b/homeassistant/components/playstation_network/strings.json @@ -158,6 +158,9 @@ "notify": { "group_message": { "name": "Group: {group_name}" + }, + "direct_message": { + "name": "Direct message" } } } diff --git a/tests/components/playstation_network/snapshots/test_notify.ambr b/tests/components/playstation_network/snapshots/test_notify.ambr index 60525925787..d8c32918433 100644 --- a/tests/components/playstation_network/snapshots/test_notify.ambr +++ b/tests/components/playstation_network/snapshots/test_notify.ambr @@ -1,4 +1,53 @@ # serializer version: 1 +# name: test_notify_platform[notify.testuser_direct_message-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'notify', + 'entity_category': None, + 'entity_id': 'notify.testuser_direct_message', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': 'Direct message', + 'platform': 'playstation_network', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': , + 'unique_id': 'fren-psn-id_direct_message', + 'unit_of_measurement': None, + }) +# --- +# name: test_notify_platform[notify.testuser_direct_message-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'friendly_name': 'testuser Direct message', + 'supported_features': , + }), + 'context': , + 'entity_id': 'notify.testuser_direct_message', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- # name: test_notify_platform[notify.testuser_group_publicuniversalfriend-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/playstation_network/test_notify.py b/tests/components/playstation_network/test_notify.py index ebaac37a09f..f81e03dfcc4 100644 --- a/tests/components/playstation_network/test_notify.py +++ b/tests/components/playstation_network/test_notify.py @@ -55,11 +55,16 @@ async def test_notify_platform( await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id) +@pytest.mark.parametrize( + "entity_id", + ["notify.testuser_group_publicuniversalfriend", "notify.testuser_direct_message"], +) @freeze_time("2025-07-28T00:00:00+00:00") async def test_send_message( hass: HomeAssistant, config_entry: MockConfigEntry, mock_psnawpapi: MagicMock, + entity_id: str, ) -> None: """Test send message.""" @@ -69,7 +74,7 @@ async def test_send_message( assert config_entry.state is ConfigEntryState.LOADED - state = hass.states.get("notify.testuser_group_publicuniversalfriend") + state = hass.states.get(entity_id) assert state assert state.state == STATE_UNKNOWN @@ -77,13 +82,13 @@ async def test_send_message( NOTIFY_DOMAIN, SERVICE_SEND_MESSAGE, { - ATTR_ENTITY_ID: "notify.testuser_group_publicuniversalfriend", + ATTR_ENTITY_ID: entity_id, ATTR_MESSAGE: "henlo fren", }, blocking=True, ) - state = hass.states.get("notify.testuser_group_publicuniversalfriend") + state = hass.states.get(entity_id) assert state assert state.state == "2025-07-28T00:00:00+00:00" mock_psnawpapi.group.return_value.send_message.assert_called_once_with("henlo fren")