From 21105e497a057a3847bb652d2d419f5f7a702949 Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Thu, 11 Jul 2024 11:00:01 -0600 Subject: [PATCH] Add button to play a random track --- custom_components/oasis_mini/button.py | 71 ++++++++++++++----- .../oasis_mini/pyoasismini/__init__.py | 26 +++++++ .../oasis_mini/pyoasismini/const.py | 2 +- pyproject.toml | 7 ++ 4 files changed, 89 insertions(+), 17 deletions(-) diff --git a/custom_components/oasis_mini/button.py b/custom_components/oasis_mini/button.py index cce1105..628dac1 100644 --- a/custom_components/oasis_mini/button.py +++ b/custom_components/oasis_mini/button.py @@ -2,7 +2,9 @@ from __future__ import annotations -from typing import Any, Coroutine +from dataclasses import dataclass +import random +from typing import Awaitable, Callable from homeassistant.components.button import ( ButtonDeviceClass, @@ -11,31 +13,68 @@ from homeassistant.components.button import ( ) from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import EntityDescription from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN from .coordinator import OasisMiniCoordinator from .entity import OasisMiniEntity +from .pyoasismini import OasisMini from .pyoasismini.const import TRACKS -class OasisMiniButtonEntity(OasisMiniEntity, ButtonEntity): - """Oasis Mini button entity.""" - - async def async_press(self) -> None: - """Press the button.""" - await self.device.async_reboot() - - -DESCRIPTOR = ButtonEntityDescription( - key="reboot", device_class=ButtonDeviceClass.RESTART -) - - async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback ) -> None: """Set up Oasis Mini button using config entry.""" coordinator: OasisMiniCoordinator = hass.data[DOMAIN][entry.entry_id] - async_add_entities([OasisMiniButtonEntity(coordinator, entry, DESCRIPTOR)]) + async_add_entities( + [ + OasisMiniButtonEntity(coordinator, entry, descriptor) + for descriptor in DESCRIPTORS + ] + ) + + +async def play_random_track(device: OasisMini) -> None: + """Play random track.""" + track = int(random.choice(list(TRACKS))) + if track not in device.playlist: + await device.async_add_track_to_playlist(track) + + # Move track to next item in the playlist and then select it + if (idx := device.playlist.index(track)) != (next_idx := device.playlist_index + 1): + await device.async_move_track(idx, next_idx) + await device.async_change_track(next_idx) + await device.async_play() + + +@dataclass(frozen=True, kw_only=True) +class OasisMiniButtonEntityDescription(ButtonEntityDescription): + """Oasis Mini button entity description.""" + + press_fn: Callable[[OasisMini], Awaitable[None]] + + +DESCRIPTORS = ( + OasisMiniButtonEntityDescription( + key="reboot", + device_class=ButtonDeviceClass.RESTART, + press_fn=lambda device: device.async_reboot(), + ), + OasisMiniButtonEntityDescription( + key="random_track", + name="Play random track", + press_fn=play_random_track, + ), +) + + +class OasisMiniButtonEntity(OasisMiniEntity, ButtonEntity): + """Oasis Mini button entity.""" + + entity_description: OasisMiniButtonEntityDescription + + async def async_press(self) -> None: + """Press the button.""" + await self.entity_description.press_fn(self.device) + await self.coordinator.async_request_refresh() diff --git a/custom_components/oasis_mini/pyoasismini/__init__.py b/custom_components/oasis_mini/pyoasismini/__init__.py index 7a2fc52..e803189 100644 --- a/custom_components/oasis_mini/pyoasismini/__init__.py +++ b/custom_components/oasis_mini/pyoasismini/__init__.py @@ -127,6 +127,11 @@ class OasisMini: """Return the url.""" return f"http://{self._host}/" + async def async_add_track_to_playlist(self, track: int) -> None: + """Add track to playlist.""" + await self._async_command(params={"ADDJOBLIST": track}) + self.playlist.append(track) + async def async_change_track(self, index: int) -> None: """Change the track.""" if index >= len(self.playlist): @@ -156,6 +161,10 @@ class OasisMini: setattr(self, attr, value) return status + async def async_move_track(self, _from: int, _to: int) -> None: + """Move a track in the playlist.""" + await self._async_command(params={"MOVEJOB": f"{_from};{_to}"}) + async def async_pause(self) -> None: """Send pause command.""" await self._async_command(params={"CMDPAUSE": ""}) @@ -264,6 +273,19 @@ class OasisMini: ) return response + async def async_cloud_get_tracks(self, tracks: list[int]) -> None: + """Get cloud tracks.""" + if not self.access_token: + return + + response = await self._async_request( + "GET", + urljoin(CLOUD_BASE_URL, "api/track"), + headers={"Authorization": f"Bearer {self.access_token}"}, + params={"ids[]": tracks}, + ) + return response + async def _async_request(self, method: str, url: str, **kwargs) -> Any: """Login via the cloud.""" response = await self._session.request(method, url, **kwargs) @@ -283,3 +305,7 @@ class OasisMini: self._current_track_details = await self.async_cloud_get_track_info( self.current_track_id ) + + async def async_get_playlist_details(self) -> dict: + """Get playlist info.""" + return await self.async_cloud_get_tracks(self.playlist) diff --git a/custom_components/oasis_mini/pyoasismini/const.py b/custom_components/oasis_mini/pyoasismini/const.py index 63d8082..a1e8360 100644 --- a/custom_components/oasis_mini/pyoasismini/const.py +++ b/custom_components/oasis_mini/pyoasismini/const.py @@ -8,4 +8,4 @@ from typing import Final __TRACKS_FILE = os.path.join(os.path.dirname(__file__), "tracks.json") with open(__TRACKS_FILE, "r", encoding="utf8") as file: - TRACKS: Final[dict[int, dict[str, str]]] = json.load(file) + TRACKS: Final[dict[str, dict[str, str]]] = json.load(file) diff --git a/pyproject.toml b/pyproject.toml index 9bc4496..a033f8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,3 +4,10 @@ known-first-party = ["homeassistant", "tests"] forced-separate = ["tests"] combine-as-imports = true split-on-trailing-comma = false + +[tool.pylint."MESSAGES CONTROL"] +# abstract-method - with intro of async there are always methods missing +disable = [ + "abstract-method", + "unexpected-keyword-arg", +] \ No newline at end of file