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>
This commit is contained in:
Joost Lekkerkerker 2023-04-19 14:13:43 +02:00 committed by GitHub
parent f3e6d6dfc0
commit 88f5f04be8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 76 deletions

View file

@ -3,10 +3,8 @@ from __future__ import annotations
import hashlib import hashlib
import logging import logging
import re
import pylast as lastfm from pylast import LastFMNetwork, Track, User, WSError
from pylast import WSError
import voluptuous as vol import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity 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.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
_LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
CONF_USERS = "users"
ATTR_LAST_PLAYED = "last_played" ATTR_LAST_PLAYED = "last_played"
ATTR_PLAY_COUNT = "play_count" ATTR_PLAY_COUNT = "play_count"
@ -24,9 +24,6 @@ ATTR_TOP_PLAYED = "top_played"
STATE_NOT_SCROBBLING = "Not Scrobbling" STATE_NOT_SCROBBLING = "Not Scrobbling"
CONF_USERS = "users"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{ {
vol.Required(CONF_API_KEY): cv.string, 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( def setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
@ -42,88 +44,46 @@ def setup_platform(
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
) -> None: ) -> None:
"""Set up the Last.fm sensor platform.""" """Set up the Last.fm sensor platform."""
api_key = config[CONF_API_KEY] lastfm_api = LastFMNetwork(api_key=config[CONF_API_KEY])
users = config[CONF_USERS]
lastfm_api = lastfm.LastFMNetwork(api_key=api_key)
entities = [] entities = []
for username in users: for username in config[CONF_USERS]:
try: try:
lastfm_api.get_user(username).get_image() user = lastfm_api.get_user(username)
entities.append(LastfmSensor(username, lastfm_api)) entities.append(LastFmSensor(user, lastfm_api))
except WSError as error: except WSError as exc:
_LOGGER.error(error) LOGGER.error("Failed to load LastFM user `%s`: %r", username, exc)
return return
add_entities(entities, True) add_entities(entities, True)
class LastfmSensor(SensorEntity): class LastFmSensor(SensorEntity):
"""A class for the Last.fm account.""" """A class for the Last.fm account."""
_attr_attribution = "Data provided by Last.fm" _attr_attribution = "Data provided by Last.fm"
_attr_icon = "mdi:radio-fm" _attr_icon = "mdi:radio-fm"
def __init__(self, user, lastfm_api): def __init__(self, user: User, lastfm_api: LastFMNetwork) -> None:
"""Initialize the sensor.""" """Initialize the sensor."""
self._unique_id = hashlib.sha256(user.encode("utf-8")).hexdigest() self._attr_unique_id = hashlib.sha256(user.name.encode("utf-8")).hexdigest()
self._user = lastfm_api.get_user(user) self._attr_name = user.name
self._name = user self._user = 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
def update(self) -> None: def update(self) -> None:
"""Update device state.""" """Update device state."""
self._cover = self._user.get_image() self._attr_entity_picture = self._user.get_image()
self._playcount = self._user.get_playcount() if now_playing := self._user.get_now_playing():
self._attr_native_value = format_track(now_playing)
if recent_tracks := self._user.get_recent_tracks(limit=2): else:
last = recent_tracks[0] self._attr_native_value = STATE_NOT_SCROBBLING
self._lastplayed = f"{last.track.artist} - {last.track.title}" top_played = None
if top_tracks := self._user.get_top_tracks(limit=1): if top_tracks := self._user.get_top_tracks(limit=1):
top = str(top_tracks[0]) top_played = format_track(top_tracks[0].item)
if (toptitle := re.search("', '(.+?)',", top)) and ( last_played = None
topartist := re.search("'(.+?)',", top) if last_tracks := self._user.get_recent_tracks(limit=1):
): last_played = format_track(last_tracks[0].track)
self._topplayed = f"{topartist.group(1)} - {toptitle.group(1)}" play_count = self._user.get_playcount()
self._attr_extra_state_attributes = {
if (now_playing := self._user.get_now_playing()) is None: ATTR_LAST_PLAYED: last_played,
self._state = STATE_NOT_SCROBBLING ATTR_PLAY_COUNT: play_count,
return ATTR_TOP_PLAYED: top_played,
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,
} }
@property
def entity_picture(self):
"""Avatar of the user."""
return self._cover

View file

@ -24,6 +24,7 @@ class MockUser:
def __init__(self, now_playing_result): def __init__(self, now_playing_result):
"""Initialize the mock.""" """Initialize the mock."""
self._now_playing_result = now_playing_result self._now_playing_result = now_playing_result
self.name = "test"
def get_playcount(self): def get_playcount(self):
"""Get mock play count.""" """Get mock play count."""
@ -48,7 +49,9 @@ class MockUser:
@pytest.fixture(name="lastfm_network") @pytest.fixture(name="lastfm_network")
def lastfm_network_fixture(): def lastfm_network_fixture():
"""Create fixture for LastFMNetwork.""" """Create fixture for LastFMNetwork."""
with patch("pylast.LastFMNetwork") as lastfm_network: with patch(
"homeassistant.components.lastfm.sensor.LastFMNetwork"
) as lastfm_network:
yield lastfm_network yield lastfm_network