From 88f5f04be804b52496f6fb2e458f51b42f95b535 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Wed, 19 Apr 2023 14:13:43 +0200 Subject: [PATCH] Refactor LastFM to use shorthand attributes (#91606) * Preliminary PR for the coordinator * Preliminary PR for the coordinator * Preliminary PR for the coordinator * Apply suggestions from code review Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * Preliminary PR for the coordinator * Preliminary PR for the coordinator * Preliminary PR for the coordinator * Preliminary PR for the coordinator * Preliminary PR for the coordinator * Preliminary PR for the coordinator * Preliminary PR for the coordinator * Apply feedback * Apply feedback * Apply feedback * Apply feedback * Apply feedback * Update homeassistant/components/lastfm/sensor.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * Update homeassistant/components/lastfm/sensor.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * Apply feedback * Apply feedback * Fix tests * Update homeassistant/components/lastfm/sensor.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * Fix tests * Fix feedback --------- Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/lastfm/sensor.py | 110 +++++++--------------- tests/components/lastfm/test_sensor.py | 5 +- 2 files changed, 39 insertions(+), 76 deletions(-) diff --git a/homeassistant/components/lastfm/sensor.py b/homeassistant/components/lastfm/sensor.py index 70f4c22cade..a25171f9c2e 100644 --- a/homeassistant/components/lastfm/sensor.py +++ b/homeassistant/components/lastfm/sensor.py @@ -3,10 +3,8 @@ from __future__ import annotations import hashlib import logging -import re -import pylast as lastfm -from pylast import WSError +from pylast import LastFMNetwork, Track, User, WSError import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity @@ -16,7 +14,9 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -_LOGGER = logging.getLogger(__name__) +LOGGER = logging.getLogger(__name__) + +CONF_USERS = "users" ATTR_LAST_PLAYED = "last_played" ATTR_PLAY_COUNT = "play_count" @@ -24,9 +24,6 @@ ATTR_TOP_PLAYED = "top_played" STATE_NOT_SCROBBLING = "Not Scrobbling" -CONF_USERS = "users" - - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Required(CONF_API_KEY): cv.string, @@ -35,6 +32,11 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( ) +def format_track(track: Track) -> str: + """Format the track.""" + return f"{track.artist} - {track.title}" + + def setup_platform( hass: HomeAssistant, config: ConfigType, @@ -42,88 +44,46 @@ def setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the Last.fm sensor platform.""" - api_key = config[CONF_API_KEY] - users = config[CONF_USERS] - - lastfm_api = lastfm.LastFMNetwork(api_key=api_key) - + lastfm_api = LastFMNetwork(api_key=config[CONF_API_KEY]) entities = [] - for username in users: + for username in config[CONF_USERS]: try: - lastfm_api.get_user(username).get_image() - entities.append(LastfmSensor(username, lastfm_api)) - except WSError as error: - _LOGGER.error(error) + user = lastfm_api.get_user(username) + entities.append(LastFmSensor(user, lastfm_api)) + except WSError as exc: + LOGGER.error("Failed to load LastFM user `%s`: %r", username, exc) return - add_entities(entities, True) -class LastfmSensor(SensorEntity): +class LastFmSensor(SensorEntity): """A class for the Last.fm account.""" _attr_attribution = "Data provided by Last.fm" _attr_icon = "mdi:radio-fm" - def __init__(self, user, lastfm_api): + def __init__(self, user: User, lastfm_api: LastFMNetwork) -> None: """Initialize the sensor.""" - self._unique_id = hashlib.sha256(user.encode("utf-8")).hexdigest() - self._user = lastfm_api.get_user(user) - self._name = user - self._lastfm = lastfm_api - self._state = "Not Scrobbling" - self._playcount = None - self._lastplayed = None - self._topplayed = None - self._cover = None - - @property - def unique_id(self): - """Return the unique ID of the sensor.""" - return self._unique_id - - @property - def name(self): - """Return the name of the sensor.""" - return self._name - - @property - def native_value(self): - """Return the state of the sensor.""" - return self._state + self._attr_unique_id = hashlib.sha256(user.name.encode("utf-8")).hexdigest() + self._attr_name = user.name + self._user = user def update(self) -> None: """Update device state.""" - self._cover = self._user.get_image() - self._playcount = self._user.get_playcount() - - if recent_tracks := self._user.get_recent_tracks(limit=2): - last = recent_tracks[0] - self._lastplayed = f"{last.track.artist} - {last.track.title}" - + self._attr_entity_picture = self._user.get_image() + if now_playing := self._user.get_now_playing(): + self._attr_native_value = format_track(now_playing) + else: + self._attr_native_value = STATE_NOT_SCROBBLING + top_played = None if top_tracks := self._user.get_top_tracks(limit=1): - top = str(top_tracks[0]) - if (toptitle := re.search("', '(.+?)',", top)) and ( - topartist := re.search("'(.+?)',", top) - ): - self._topplayed = f"{topartist.group(1)} - {toptitle.group(1)}" - - if (now_playing := self._user.get_now_playing()) is None: - self._state = STATE_NOT_SCROBBLING - return - - self._state = f"{now_playing.artist} - {now_playing.title}" - - @property - def extra_state_attributes(self): - """Return the state attributes.""" - return { - ATTR_LAST_PLAYED: self._lastplayed, - ATTR_PLAY_COUNT: self._playcount, - ATTR_TOP_PLAYED: self._topplayed, + top_played = format_track(top_tracks[0].item) + last_played = None + if last_tracks := self._user.get_recent_tracks(limit=1): + last_played = format_track(last_tracks[0].track) + play_count = self._user.get_playcount() + self._attr_extra_state_attributes = { + ATTR_LAST_PLAYED: last_played, + ATTR_PLAY_COUNT: play_count, + ATTR_TOP_PLAYED: top_played, } - - @property - def entity_picture(self): - """Avatar of the user.""" - return self._cover diff --git a/tests/components/lastfm/test_sensor.py b/tests/components/lastfm/test_sensor.py index 6458e617dc0..0fa45a12277 100644 --- a/tests/components/lastfm/test_sensor.py +++ b/tests/components/lastfm/test_sensor.py @@ -24,6 +24,7 @@ class MockUser: def __init__(self, now_playing_result): """Initialize the mock.""" self._now_playing_result = now_playing_result + self.name = "test" def get_playcount(self): """Get mock play count.""" @@ -48,7 +49,9 @@ class MockUser: @pytest.fixture(name="lastfm_network") def lastfm_network_fixture(): """Create fixture for LastFMNetwork.""" - with patch("pylast.LastFMNetwork") as lastfm_network: + with patch( + "homeassistant.components.lastfm.sensor.LastFMNetwork" + ) as lastfm_network: yield lastfm_network