Add binary sensor platform to PlayStation Network Integration (#147639)

This commit is contained in:
Jack Powell 2025-07-05 11:27:23 -04:00 committed by GitHub
parent 4f4ec6f41a
commit 736865c130
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 217 additions and 32 deletions

View File

@ -9,7 +9,11 @@ from .const import CONF_NPSSO
from .coordinator import PlaystationNetworkConfigEntry, PlaystationNetworkCoordinator from .coordinator import PlaystationNetworkConfigEntry, PlaystationNetworkCoordinator
from .helpers import PlaystationNetwork from .helpers import PlaystationNetwork
PLATFORMS: list[Platform] = [Platform.MEDIA_PLAYER, Platform.SENSOR] PLATFORMS: list[Platform] = [
Platform.BINARY_SENSOR,
Platform.MEDIA_PLAYER,
Platform.SENSOR,
]
async def async_setup_entry( async def async_setup_entry(

View File

@ -0,0 +1,71 @@
"""Binary Sensor platform for PlayStation Network integration."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from enum import StrEnum
from homeassistant.components.binary_sensor import (
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import PlaystationNetworkConfigEntry, PlaystationNetworkData
from .entity import PlaystationNetworkServiceEntity
PARALLEL_UPDATES = 0
@dataclass(kw_only=True, frozen=True)
class PlaystationNetworkBinarySensorEntityDescription(BinarySensorEntityDescription):
"""PlayStation Network binary sensor description."""
is_on_fn: Callable[[PlaystationNetworkData], bool]
class PlaystationNetworkBinarySensor(StrEnum):
"""PlayStation Network binary sensors."""
PS_PLUS_STATUS = "ps_plus_status"
BINARY_SENSOR_DESCRIPTIONS: tuple[
PlaystationNetworkBinarySensorEntityDescription, ...
] = (
PlaystationNetworkBinarySensorEntityDescription(
key=PlaystationNetworkBinarySensor.PS_PLUS_STATUS,
translation_key=PlaystationNetworkBinarySensor.PS_PLUS_STATUS,
is_on_fn=lambda psn: psn.profile["isPlus"],
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: PlaystationNetworkConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the binary sensor platform."""
coordinator = config_entry.runtime_data
async_add_entities(
PlaystationNetworkBinarySensorEntity(coordinator, description)
for description in BINARY_SENSOR_DESCRIPTIONS
)
class PlaystationNetworkBinarySensorEntity(
PlaystationNetworkServiceEntity,
BinarySensorEntity,
):
"""Representation of a PlayStation Network binary sensor entity."""
entity_description: PlaystationNetworkBinarySensorEntityDescription
@property
def is_on(self) -> bool:
"""Return the state of the binary sensor."""
return self.entity_description.is_on_fn(self.coordinator.data)

View File

@ -0,0 +1,36 @@
"""Base entity for PlayStation Network Integration."""
from typing import TYPE_CHECKING
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import PlaystationNetworkCoordinator
class PlaystationNetworkServiceEntity(CoordinatorEntity[PlaystationNetworkCoordinator]):
"""Common entity class for PlayStationNetwork Service entities."""
_attr_has_entity_name = True
def __init__(
self,
coordinator: PlaystationNetworkCoordinator,
entity_description: EntityDescription,
) -> None:
"""Initialize PlayStation Network Service Entity."""
super().__init__(coordinator)
if TYPE_CHECKING:
assert coordinator.config_entry.unique_id
self.entity_description = entity_description
self._attr_unique_id = (
f"{coordinator.config_entry.unique_id}_{entity_description.key}"
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.config_entry.unique_id)},
name=coordinator.data.username,
entry_type=DeviceEntryType.SERVICE,
manufacturer="Sony Interactive Entertainment",
)

View File

@ -5,6 +5,11 @@
"default": "mdi:sony-playstation" "default": "mdi:sony-playstation"
} }
}, },
"binary_sensor": {
"ps_plus_status": {
"default": "mdi:shape-plus-outline"
}
},
"sensor": { "sensor": {
"trophy_level": { "trophy_level": {
"default": "mdi:trophy-award" "default": "mdi:trophy-award"

View File

@ -6,7 +6,6 @@ from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from enum import StrEnum from enum import StrEnum
from typing import TYPE_CHECKING
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
SensorDeviceClass, SensorDeviceClass,
@ -15,18 +14,12 @@ from homeassistant.components.sensor import (
) )
from homeassistant.const import PERCENTAGE from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from .const import DOMAIN from .coordinator import PlaystationNetworkConfigEntry, PlaystationNetworkData
from .coordinator import ( from .entity import PlaystationNetworkServiceEntity
PlaystationNetworkConfigEntry,
PlaystationNetworkCoordinator,
PlaystationNetworkData,
)
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0
@ -146,32 +139,12 @@ async def async_setup_entry(
class PlaystationNetworkSensorEntity( class PlaystationNetworkSensorEntity(
CoordinatorEntity[PlaystationNetworkCoordinator], SensorEntity PlaystationNetworkServiceEntity,
SensorEntity,
): ):
"""Representation of a PlayStation Network sensor entity.""" """Representation of a PlayStation Network sensor entity."""
entity_description: PlaystationNetworkSensorEntityDescription entity_description: PlaystationNetworkSensorEntityDescription
coordinator: PlaystationNetworkCoordinator
_attr_has_entity_name = True
def __init__(
self,
coordinator: PlaystationNetworkCoordinator,
description: PlaystationNetworkSensorEntityDescription,
) -> None:
"""Initialize a sensor entity."""
super().__init__(coordinator)
self.entity_description = description
if TYPE_CHECKING:
assert coordinator.config_entry.unique_id
self._attr_unique_id = f"{coordinator.config_entry.unique_id}_{description.key}"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.config_entry.unique_id)},
name=coordinator.data.username,
entry_type=DeviceEntryType.SERVICE,
manufacturer="Sony Interactive Entertainment",
)
@property @property
def native_value(self) -> StateType | datetime: def native_value(self) -> StateType | datetime:

View File

@ -53,6 +53,11 @@
} }
}, },
"entity": { "entity": {
"binary_sensor": {
"ps_plus_status": {
"name": "Subscribed to PlayStation Plus"
}
},
"sensor": { "sensor": {
"trophy_level": { "trophy_level": {
"name": "Trophy level" "name": "Trophy level"

View File

@ -0,0 +1,49 @@
# serializer version: 1
# name: test_sensors[binary_sensor.testuser_subscribed_to_playstation_plus-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'binary_sensor',
'entity_category': None,
'entity_id': 'binary_sensor.testuser_subscribed_to_playstation_plus',
'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': 'Subscribed to PlayStation Plus',
'platform': 'playstation_network',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': <PlaystationNetworkBinarySensor.PS_PLUS_STATUS: 'ps_plus_status'>,
'unique_id': 'my-psn-id_ps_plus_status',
'unit_of_measurement': None,
})
# ---
# name: test_sensors[binary_sensor.testuser_subscribed_to_playstation_plus-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'testuser Subscribed to PlayStation Plus',
}),
'context': <ANY>,
'entity_id': 'binary_sensor.testuser_subscribed_to_playstation_plus',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

View File

@ -0,0 +1,42 @@
"""Test the Playstation Network binary sensor platform."""
from collections.abc import Generator
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, snapshot_platform
@pytest.fixture(autouse=True)
def binary_sensor_only() -> Generator[None]:
"""Enable only the binary sensor platform."""
with patch(
"homeassistant.components.playstation_network.PLATFORMS",
[Platform.BINARY_SENSOR],
):
yield
@pytest.mark.usefixtures("mock_psnawpapi")
async def test_sensors(
hass: HomeAssistant,
config_entry: MockConfigEntry,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
) -> None:
"""Test setup of the PlayStation Network binary sensor platform."""
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.LOADED
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)