1
0
mirror of https://github.com/natekspencer/hacs-oasis_mini.git synced 2025-11-13 07:33:51 -05:00

Better error handling

This commit is contained in:
Nathan Spencer
2024-08-03 17:31:30 -06:00
parent a44c035828
commit 33e62528ba
9 changed files with 1978 additions and 828 deletions

View File

@@ -39,7 +39,7 @@ async def async_setup_entry(
async def play_random_track(device: OasisMini) -> None: async def play_random_track(device: OasisMini) -> None:
"""Play random track.""" """Play random track."""
track = int(random.choice(list(TRACKS))) track = random.choice(list(TRACKS))
await add_and_play_track(device, track) await add_and_play_track(device, track)

View File

@@ -47,15 +47,14 @@ class OasisMiniCoordinator(DataUpdateCoordinator[str]):
if not self.device.software_version: if not self.device.software_version:
await self.device.async_get_software_version() await self.device.async_get_software_version()
data = await self.device.async_get_status() data = await self.device.async_get_status()
self.attempt = 0
await self.device.async_get_current_track_details() await self.device.async_get_current_track_details()
await self.device.async_get_playlist_details() await self.device.async_get_playlist_details()
except Exception as ex: # pylint:disable=broad-except except Exception as ex: # pylint:disable=broad-except
if self.attempt > 2 or not self.data: if self.attempt > 2 or not (data or self.data):
raise UpdateFailed( raise UpdateFailed(
f"Couldn't read from the Oasis Mini after {self.attempt} attempts" f"Couldn't read from the Oasis Mini after {self.attempt} attempts"
) from ex ) from ex
else:
self.attempt = 0
if data != self.data: if data != self.data:
self.last_updated = datetime.now() self.last_updated = datetime.now()

View File

@@ -6,6 +6,7 @@ from homeassistant.components.image import Image, ImageEntity, ImageEntityDescri
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import UNDEFINED
from .const import DOMAIN from .const import DOMAIN
from .coordinator import OasisMiniCoordinator from .coordinator import OasisMiniCoordinator
@@ -52,13 +53,18 @@ class OasisMiniImageEntity(OasisMiniEntity, ImageEntity):
self._track_id = self.device.track_id self._track_id = self.device.track_id
self._progress = self.device.progress self._progress = self.device.progress
self._cached_image = None self._cached_image = None
if not self.device.access_token: if self.device.track and self.device.track.get("svg_content"):
self._attr_image_url = UNDEFINED
else:
self._attr_image_url = ( self._attr_image_url = (
f"https://app.grounded.so/uploads/{track['image']}" f"https://app.grounded.so/uploads/{track['image']}"
if (track := TRACKS.get(str(self.device.track_id))) if (
track := (self.device.track or TRACKS.get(self.device.track_id))
)
and "image" in track and "image" in track
else None else None
) )
if self.hass: if self.hass:
super()._handle_coordinator_update() super()._handle_coordinator_update()

View File

@@ -23,7 +23,6 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN from .const import DOMAIN
from .coordinator import OasisMiniCoordinator from .coordinator import OasisMiniCoordinator
from .entity import OasisMiniEntity from .entity import OasisMiniEntity
from .helpers import add_and_play_track
from .pyoasismini.const import TRACKS from .pyoasismini.const import TRACKS
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -61,7 +60,7 @@ class OasisMiniMediaPlayerEntity(OasisMiniEntity, MediaPlayerEntity):
def media_image_url(self) -> str | None: def media_image_url(self) -> str | None:
"""Image url of current playing media.""" """Image url of current playing media."""
if not (track := self.device.track): if not (track := self.device.track):
track = TRACKS.get(str(self.device.track_id)) track = TRACKS.get(self.device.track_id)
if track and "image" in track: if track and "image" in track:
return f"https://app.grounded.so/uploads/{track['image']}" return f"https://app.grounded.so/uploads/{track['image']}"
return None return None
@@ -82,7 +81,7 @@ class OasisMiniMediaPlayerEntity(OasisMiniEntity, MediaPlayerEntity):
if not self.device.track_id: if not self.device.track_id:
return None return None
if not (track := self.device.track): if not (track := self.device.track):
track = TRACKS.get(str(self.device.track_id), {}) track = TRACKS.get(self.device.track_id, {})
return track.get("name", f"Unknown Title (#{self.device.track_id})") return track.get("name", f"Unknown Title (#{self.device.track_id})")
@property @property
@@ -153,7 +152,7 @@ class OasisMiniMediaPlayerEntity(OasisMiniEntity, MediaPlayerEntity):
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
"""Play a piece of media.""" """Play a piece of media."""
if media_id not in TRACKS: if media_id not in map(str, TRACKS):
media_id = next( media_id = next(
( (
id id
@@ -176,13 +175,18 @@ class OasisMiniMediaPlayerEntity(OasisMiniEntity, MediaPlayerEntity):
if enqueue in (MediaPlayerEnqueue.NEXT, MediaPlayerEnqueue.PLAY): if enqueue in (MediaPlayerEnqueue.NEXT, MediaPlayerEnqueue.PLAY):
# Move track to next item in the playlist # Move track to next item in the playlist
if (idx := (len(device.playlist) - 1)) != device.playlist_index: if (index := (len(device.playlist) - 1)) != device.playlist_index:
if idx != (nxt := min(device.playlist_index + 1, len(device.playlist))): if index != (
await device.async_move_track(idx, nxt) _next := min(device.playlist_index + 1, len(device.playlist) - 1)
):
await device.async_move_track(index, _next)
if enqueue == MediaPlayerEnqueue.PLAY: if enqueue == MediaPlayerEnqueue.PLAY:
await device.async_change_track(nxt) await device.async_change_track(_next)
if device.status_code != 4: if (
enqueue in (MediaPlayerEnqueue.PLAY, MediaPlayerEnqueue.REPLACE)
and device.status_code != 4
):
await device.async_play() await device.async_play()
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()

View File

@@ -7,6 +7,7 @@ from urllib.parse import urljoin
from aiohttp import ClientResponseError, ClientSession from aiohttp import ClientResponseError, ClientSession
from .const import TRACKS
from .utils import _bit_to_bool from .utils import _bit_to_bool
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -183,7 +184,6 @@ class OasisMini:
playlist = [t for t in self.playlist if t] + [track] playlist = [t for t in self.playlist if t] + [track]
return await self.async_set_playlist(playlist) return await self.async_set_playlist(playlist)
_LOGGER.debug("Adding track %s to playlist", track)
await self._async_command(params={"ADDJOBLIST": track}) await self._async_command(params={"ADDJOBLIST": track})
self.playlist.append(track) self.playlist.append(track)
@@ -346,7 +346,7 @@ class OasisMini:
return {"id": track_id, "name": f"Unknown Title (#{track_id})"} return {"id": track_id, "name": f"Unknown Title (#{track_id})"}
except Exception as ex: except Exception as ex:
_LOGGER.exception(ex) _LOGGER.exception(ex)
return None return None
async def async_cloud_get_tracks( async def async_cloud_get_tracks(
self, tracks: list[int] | None = None self, tracks: list[int] | None = None
@@ -355,6 +355,8 @@ class OasisMini:
response = await self._async_cloud_request( response = await self._async_cloud_request(
"GET", "api/track", params={"ids[]": tracks or []} "GET", "api/track", params={"ids[]": tracks or []}
) )
if not response:
return None
track_details = response.get("data", []) track_details = response.get("data", [])
while next_page_url := response.get("next_page_url"): while next_page_url := response.get("next_page_url"):
response = await self._async_cloud_request("GET", next_page_url) response = await self._async_cloud_request("GET", next_page_url)
@@ -367,17 +369,22 @@ class OasisMini:
async def async_get_current_track_details(self) -> dict | None: async def async_get_current_track_details(self) -> dict | None:
"""Get current track info, refreshing if needed.""" """Get current track info, refreshing if needed."""
if (track := self._track) and track.get("id") == self.track_id: track_id = self.track_id
if (track := self._track) and track.get("id") == track_id:
return track return track
if self.track_id: if track_id:
self._track = await self.async_cloud_get_track_info(self.track_id) self._track = await self.async_cloud_get_track_info(track_id)
if not self._track:
self._track = TRACKS.get(
track_id, {"id": track_id, "name": f"Unknown Title (#{track_id})"}
)
return self._track return self._track
async def async_get_playlist_details(self) -> dict[int, dict[str, str]]: async def async_get_playlist_details(self) -> dict[int, dict[str, str]]:
"""Get playlist info.""" """Get playlist info."""
if set(self.playlist).difference(self._playlist.keys()): if set(self.playlist).difference(self._playlist.keys()):
tracks = await self.async_cloud_get_tracks(self.playlist) tracks = await self.async_cloud_get_tracks(self.playlist)
self._playlist = { all_tracks = TRACKS | {
track["id"]: { track["id"]: {
"name": track["name"], "name": track["name"],
"author": ((track.get("author") or {}).get("person") or {}).get( "author": ((track.get("author") or {}).get("person") or {}).get(
@@ -387,6 +394,10 @@ class OasisMini:
} }
for track in tracks for track in tracks
} }
for track in self.playlist:
self._playlist[track] = all_tracks.get(
track, {"name": f"Unknown Title (#{track})"}
)
return self._playlist return self._playlist
async def _async_cloud_request(self, method: str, url: str, **kwargs: Any) -> Any: async def _async_cloud_request(self, method: str, url: str, **kwargs: Any) -> Any:
@@ -412,6 +423,13 @@ class OasisMini:
async def _async_request(self, method: str, url: str, **kwargs) -> Any: async def _async_request(self, method: str, url: str, **kwargs) -> Any:
"""Perform a request.""" """Perform a request."""
_LOGGER.debug(
"%s %s",
method,
self._session._build_url(url).update_query( # pylint: disable=protected-access
kwargs.get("params")
),
)
response = await self._session.request(method, url, **kwargs) response = await self._session.request(method, url, **kwargs)
if response.status == 200: if response.status == 200:
if response.content_type == "application/json": if response.content_type == "application/json":

View File

@@ -4,8 +4,13 @@ from __future__ import annotations
import json import json
import os import os
from typing import Final from typing import Any, Final
__TRACKS_FILE = os.path.join(os.path.dirname(__file__), "tracks.json") __TRACKS_FILE = os.path.join(os.path.dirname(__file__), "tracks.json")
with open(__TRACKS_FILE, "r", encoding="utf8") as file: try:
TRACKS: Final[dict[str, dict[str, str]]] = json.load(file) with open(__TRACKS_FILE, "r", encoding="utf8") as file:
TRACKS: Final[dict[int, dict[str, Any]]] = {
int(k): v for k, v in json.load(file).items()
}
except Exception: # ignore: broad-except
TRACKS = {}

File diff suppressed because it is too large Load Diff

View File

@@ -72,7 +72,7 @@ def playlist_update_handler(entity: OasisMiniSelectEntity) -> None:
options = [ options = [
device._playlist.get(track, {}).get( device._playlist.get(track, {}).get(
"name", "name",
TRACKS.get(str(track), {}).get( TRACKS.get(track, {"id": track, "name": f"Unknown Title (#{track})"}).get(
"name", "name",
device.track["name"] device.track["name"]
if device.track and device.track["id"] == track if device.track and device.track["id"] == track

View File

@@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
from datetime import timedelta from datetime import timedelta
import logging
from typing import Any from typing import Any
from homeassistant.components.update import ( from homeassistant.components.update import (
@@ -19,6 +20,8 @@ from .const import DOMAIN
from .coordinator import OasisMiniCoordinator from .coordinator import OasisMiniCoordinator
from .entity import OasisMiniEntity from .entity import OasisMiniEntity
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(hours=6) SCAN_INTERVAL = timedelta(hours=6)
@@ -75,6 +78,9 @@ class OasisMiniUpdateEntity(OasisMiniEntity, UpdateEntity):
"""Update the entity.""" """Update the entity."""
await self.device.async_get_software_version() await self.device.async_get_software_version()
software = await self.device.async_cloud_get_latest_software_details() software = await self.device.async_cloud_get_latest_software_details()
if not software:
_LOGGER.warning("Unable to get latest software details")
return
self._attr_latest_version = software["version"] self._attr_latest_version = software["version"]
self._attr_release_summary = software["description"] self._attr_release_summary = software["description"]
self._attr_release_url = f"https://app.grounded.so/software/{software['id']}" self._attr_release_url = f"https://app.grounded.so/software/{software['id']}"