From 1003a5fda2244f47a38563af9f96f06016f8009c Mon Sep 17 00:00:00 2001 From: Jack Powell Date: Sat, 16 Nov 2024 18:01:26 +0000 Subject: [PATCH] Include additional profile data --- .../playstation_network/__init__.py | 23 +++----- .../playstation_network/binary_sensor.py | 43 +++++++++++++++ .../playstation_network/coordinator.py | 2 +- .../playstation_network/image.py | 55 +++++++++++++++++++ .../playstation_network/sensor.py | 50 ++++------------- 5 files changed, 118 insertions(+), 55 deletions(-) create mode 100644 custom_components/playstation_network/binary_sensor.py create mode 100644 custom_components/playstation_network/image.py diff --git a/custom_components/playstation_network/__init__.py b/custom_components/playstation_network/__init__.py index 2f936ef..8c7e8e8 100644 --- a/custom_components/playstation_network/__init__.py +++ b/custom_components/playstation_network/__init__.py @@ -21,6 +21,8 @@ PLATFORMS: list[Platform] = [ Platform.MEDIA_PLAYER, Platform.SENSOR, + Platform.BINARY_SENSOR, + Platform.IMAGE, ] _LOGGER: logging.Logger = logging.getLogger(__package__) @@ -73,26 +75,14 @@ def async_migrate_entity_entry(entry: er.RegistryEntry) -> dict[str, Any] | None """ if entry.domain == Platform.SENSOR and entry.unique_id == "psn_psn_status": new = f"{coordinator.data.get("username").lower()}_psn_status" - return { - "new_unique_id": entry.unique_id.replace( - "psn_psn_status", new - ) - } + return {"new_unique_id": entry.unique_id.replace("psn_psn_status", new)} if entry.domain == Platform.SENSOR and entry.unique_id == "psn_psn_trophies": new = f"{coordinator.data.get("username").lower()}_psn_trophy_level" - return { - "new_unique_id": entry.unique_id.replace( - "psn_psn_trophies", new - ) - } + return {"new_unique_id": entry.unique_id.replace("psn_psn_trophies", new)} if entry.domain == Platform.MEDIA_PLAYER and entry.unique_id == "PS5_console": new = f"{coordinator.data.get('username').lower()}_{coordinator.data.get('platform').get('platform').lower()}_console" - return { - "new_unique_id": entry.unique_id.replace( - "PS5_console", new - ) - } + return {"new_unique_id": entry.unique_id.replace("PS5_console", new)} # No migration needed return None @@ -105,7 +95,6 @@ def async_migrate_entity_entry(entry: er.RegistryEntry) -> dict[str, Any] | None _migrate_device_identifiers(hass, entry.entry_id, coordinator) hass.config_entries.async_update_entry(entry, version=2) - await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(update_listener)) return True @@ -123,10 +112,12 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry): """Update Listener.""" await hass.config_entries.async_reload(entry.entry_id) + async def async_migrate_entry(hass: HomeAssistant, self): """Migrate Entry Support""" return True + def _migrate_device_identifiers( hass: HomeAssistant, entry_id: str, coordinator ) -> None: diff --git a/custom_components/playstation_network/binary_sensor.py b/custom_components/playstation_network/binary_sensor.py new file mode 100644 index 0000000..6820e82 --- /dev/null +++ b/custom_components/playstation_network/binary_sensor.py @@ -0,0 +1,43 @@ +"""Binary sensor platform for Unfolded Circle.""" + +from homeassistant.components.binary_sensor import BinarySensorEntity +from homeassistant.core import HomeAssistant, callback + +from .const import DOMAIN, PSN_COORDINATOR +from .entity import PSNEntity + + +async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): + """Add sensors for passed config_entry in HA.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id][PSN_COORDINATOR] + + async_add_entities([PlaystationPlusBinarySensor(coordinator)]) + + +class PlaystationPlusBinarySensor(PSNEntity, BinarySensorEntity): + """Sensor indicating if user is subscribed to PlayStation Plus""" + + def __init__(self, coordinator) -> None: + """Initialize Binary Sensor.""" + super().__init__(coordinator) + self.coordinator = coordinator + + # As per the sensor, this must be a unique value within this domain. + self._attr_unique_id = ( + f"{coordinator.data.get("username").lower()}_has_playstation_plus" + ) + + # The name of the entity + self._attr_has_entity_name = True + self._attr_name = "Playstation Plus" + self._attr_native_value = self.coordinator.data.get("profile").get("isPlus") + self._attr_icon = "mdi:gamepad-outline" + + @property + def is_on(self): + """Return the state of the binary sensor.""" + return self.coordinator.data.get("profile", {}).get("isPlus") + + @callback + def _handle_coordinator_update(self) -> None: + self.async_write_ha_state() diff --git a/custom_components/playstation_network/coordinator.py b/custom_components/playstation_network/coordinator.py index a373d86..a3fa1fa 100644 --- a/custom_components/playstation_network/coordinator.py +++ b/custom_components/playstation_network/coordinator.py @@ -58,7 +58,7 @@ async def _async_update_data(self) -> dict[str, Any]: try: self.data["username"] = self.user.online_id self.data["profile"] = await self.hass.async_add_executor_job( - self.user.profile + lambda: self.user.profile() ) self.data["presence"] = await self.hass.async_add_executor_job( self.user.get_presence diff --git a/custom_components/playstation_network/image.py b/custom_components/playstation_network/image.py new file mode 100644 index 0000000..27d9995 --- /dev/null +++ b/custom_components/playstation_network/image.py @@ -0,0 +1,55 @@ +"""Image sensor platform for the PlayStation Network.""" + +from homeassistant.components.image import ImageEntity +from homeassistant.core import HomeAssistant, callback +from homeassistant.util import dt as dt_util + +from .const import DOMAIN, PSN_COORDINATOR +from .entity import PSNEntity + + +async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): + """Add sensors for passed config_entry in HA.""" + coordinator = hass.data[DOMAIN][config_entry.entry_id][PSN_COORDINATOR] + + async_add_entities([PlaystationProfileImage(coordinator)]) + + +class PlaystationProfileImage(PSNEntity, ImageEntity): + """Image representing your PlayStation Avatar""" + + def __init__(self, coordinator) -> None: + """Initialize Image.""" + super().__init__(coordinator) + self._attr_extra_state_attributes = {} + self.coordinator = coordinator + + self._attr_unique_id = ( + f"{self.coordinator.data.get("username").lower()}_playstation_avatar" + ) + + ImageEntity.__init__(self, coordinator.hass) + self._attr_has_entity_name = True + self._attr_name = "Playstation Avatar" + self._attr_icon = "mdi:face-man-profile" + self._attr_image_url = self.get_avatar() + self._attr_image_last_updated = dt_util.utcnow() + + def get_avatar(self) -> str: + """Return Avatar URL""" + previous_image = self._attr_image_url + current_image = None + for avatar in self.coordinator.data.get("profile", {}).get("avatars", []): + if avatar.get("size") == "xl": + current_image = avatar.get("url", None) + + if previous_image != current_image: + self._cached_image = None + self._attr_image_last_updated = dt_util.utcnow() + + return current_image + + @callback + def _handle_coordinator_update(self) -> None: + self._attr_image_url = self.get_avatar() + self.async_write_ha_state() diff --git a/custom_components/playstation_network/sensor.py b/custom_components/playstation_network/sensor.py index c479dda..ebe2ca1 100644 --- a/custom_components/playstation_network/sensor.py +++ b/custom_components/playstation_network/sensor.py @@ -46,13 +46,6 @@ def get_status(coordinator_data: any) -> str: return "Offline" -def get_avatar(data: any) -> str: - """Return Avatar URL""" - for avatar in data.get("profile").get("avatars"): - if avatar.get("size") == "l": - return avatar.get("url") - - def get_status_attr(coordinator_data: any) -> dict[str, str]: """Parses status attributes""" attrs: dict[str, str] = { @@ -64,8 +57,6 @@ def get_status_attr(coordinator_data: any) -> dict[str, str]: "play_duration": None, "star_rating": None, "about_me": None, - "avatar": None, - "playstation_plus": None, "trophies": { "platinum": None, "gold": None, @@ -82,15 +73,23 @@ def get_status_attr(coordinator_data: any) -> dict[str, str]: } attrs["next_level_progress"] = coordinator_data.get("trophy_summary").progress + attrs["about_me"] = coordinator_data.get("profile").get("aboutMe") if coordinator_data.get("title_metadata", {}).get("npTitleId"): title = coordinator_data.get("title_details", [{}])[0] title_trophies = coordinator_data.get("title_trophies", {}) attrs["name"] = title.get("name", "").title() - attrs["description"] = ( - title.get("descriptions", [""])[0].get("desc", "").title() - ) + + description = "" + for desc in title.get("descriptions", [""]): + if desc.get("type") == "SHORT": + description = desc.get("desc", "").title() + + if len(description) >= 252: + description = f"{description[0:252]}..." + attrs["description"] = description + attrs["platform"] = ( coordinator_data.get("presence", {}) .get("basicPresence", {}) @@ -103,11 +102,6 @@ def get_status_attr(coordinator_data: any) -> dict[str, str]: attrs["earned_trophies"] = title_trophies.earned_trophies attrs["trophy_progress"] = title_trophies.progress - attrs["about_me"] = coordinator_data.get("profile").get("aboutMe") - attrs["playstation_plus"] = coordinator_data.get("profile").get("isPlus") - - attrs["avatar"] = get_avatar(coordinator_data) - for t in coordinator_data["recent_titles"]: if t.title_id == coordinator_data.get("title_metadata").get("npTitleId"): title_stats = t @@ -195,27 +189,7 @@ def get_trophy_attr(coordinator_data: any) -> dict[str, str]: entity_registry_enabled_default=True, has_entity_name=True, unique_id="psn_about_me_attr", - value_fn=lambda data: data.get("profile").get("aboutMe"), - ), - PsnSensorEntityDescription( - key="has_playstation_plus", - native_unit_of_measurement=None, - name="Playstation Plus", - icon="mdi:gamepad-outline", - entity_registry_enabled_default=True, - has_entity_name=True, - unique_id="psn_playstation_plus_attr", - value_fn=lambda data: data.get("profile").get("isPlus"), - ), - PsnSensorEntityDescription( - key="avatar", - native_unit_of_measurement=None, - name="Avatar", - icon="mdi:face-man-profile", - entity_registry_enabled_default=True, - has_entity_name=True, - unique_id="psn_avatar_attr", - value_fn=get_avatar, + value_fn=lambda data: data.get("about_me"), ), PsnSensorEntityDescription( key="platform",