mirror of
https://github.com/natekspencer/hacs-oasis_mini.git
synced 2025-12-06 18:44:14 -05:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
379b6f67f2 |
@@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import async_timeout
|
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
@@ -68,7 +67,7 @@ class OasisDeviceCoordinator(DataUpdateCoordinator[list[OasisDevice]]):
|
|||||||
self.attempt += 1
|
self.attempt += 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with async_timeout.timeout(30):
|
async with asyncio.timeout(30):
|
||||||
raw_devices = await self.cloud_client.async_get_devices()
|
raw_devices = await self.cloud_client.async_get_devices()
|
||||||
|
|
||||||
existing_by_serial = {
|
existing_by_serial = {
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import async_timeout
|
|
||||||
|
|
||||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
|
|
||||||
from .pyoasiscontrol import OasisCloudClient, OasisDevice
|
from .pyoasiscontrol import OasisCloudClient, OasisDevice
|
||||||
from .pyoasiscontrol.const import TRACKS
|
from .pyoasiscontrol.const import STATUS_PLAYING, TRACKS
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -44,24 +42,26 @@ async def add_and_play_track(device: OasisDevice, track: int) -> None:
|
|||||||
track (int): The track id to add and play.
|
track (int): The track id to add and play.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
async_timeout.TimeoutError: If the operation does not complete within 10 seconds.
|
TimeoutError: If the operation does not complete within 10 seconds.
|
||||||
"""
|
"""
|
||||||
async with async_timeout.timeout(10):
|
async with asyncio.timeout(10):
|
||||||
if track not in device.playlist:
|
if track not in device.playlist:
|
||||||
await device.async_add_track_to_playlist(track)
|
await device.async_add_track_to_playlist(track)
|
||||||
|
|
||||||
|
# Wait for device state to reflect the newly added track
|
||||||
while track not in device.playlist:
|
while track not in device.playlist:
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
# Move track to next item in the playlist and then select it
|
# Ensure the track is positioned immediately after the current track and select it
|
||||||
if (index := device.playlist.index(track)) != device.playlist_index:
|
if (index := device.playlist.index(track)) != device.playlist_index:
|
||||||
|
# Calculate the position after the current track
|
||||||
if index != (
|
if index != (
|
||||||
_next := min(device.playlist_index + 1, len(device.playlist) - 1)
|
_next := min(device.playlist_index + 1, len(device.playlist) - 1)
|
||||||
):
|
):
|
||||||
await device.async_move_track(index, _next)
|
await device.async_move_track(index, _next)
|
||||||
await device.async_change_track(_next)
|
await device.async_change_track(_next)
|
||||||
|
|
||||||
if device.status_code != 4:
|
if device.status_code != STATUS_PLAYING:
|
||||||
await device.async_play()
|
await device.async_play()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,16 @@ from .const import DOMAIN
|
|||||||
from .entity import OasisDeviceEntity
|
from .entity import OasisDeviceEntity
|
||||||
from .helpers import get_track_id
|
from .helpers import get_track_id
|
||||||
from .pyoasiscontrol import OasisDevice
|
from .pyoasiscontrol import OasisDevice
|
||||||
|
from .pyoasiscontrol.const import (
|
||||||
|
STATUS_CENTERING,
|
||||||
|
STATUS_DOWNLOADING,
|
||||||
|
STATUS_ERROR,
|
||||||
|
STATUS_LIVE,
|
||||||
|
STATUS_PAUSED,
|
||||||
|
STATUS_PLAYING,
|
||||||
|
STATUS_STOPPED,
|
||||||
|
STATUS_UPDATING,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@@ -130,17 +140,17 @@ class OasisDeviceMediaPlayerEntity(OasisDeviceEntity, MediaPlayerEntity):
|
|||||||
def state(self) -> MediaPlayerState:
|
def state(self) -> MediaPlayerState:
|
||||||
"""State of the player."""
|
"""State of the player."""
|
||||||
status_code = self.device.status_code
|
status_code = self.device.status_code
|
||||||
if self.device.error or status_code in (9, 11):
|
if self.device.error or status_code in (STATUS_ERROR, STATUS_UPDATING):
|
||||||
return MediaPlayerState.OFF
|
return MediaPlayerState.OFF
|
||||||
if status_code == 2:
|
if status_code == STATUS_STOPPED:
|
||||||
return MediaPlayerState.IDLE
|
return MediaPlayerState.IDLE
|
||||||
if status_code in (3, 13):
|
if status_code in (STATUS_CENTERING, STATUS_DOWNLOADING):
|
||||||
return MediaPlayerState.BUFFERING
|
return MediaPlayerState.BUFFERING
|
||||||
if status_code == 4:
|
if status_code == STATUS_PLAYING:
|
||||||
return MediaPlayerState.PLAYING
|
return MediaPlayerState.PLAYING
|
||||||
if status_code == 5:
|
if status_code == STATUS_PAUSED:
|
||||||
return MediaPlayerState.PAUSED
|
return MediaPlayerState.PAUSED
|
||||||
if status_code == 15:
|
if status_code == STATUS_LIVE:
|
||||||
return MediaPlayerState.ON
|
return MediaPlayerState.ON
|
||||||
return MediaPlayerState.IDLE
|
return MediaPlayerState.IDLE
|
||||||
|
|
||||||
|
|||||||
@@ -12,19 +12,19 @@ try:
|
|||||||
TRACKS: Final[dict[int, dict[str, Any]]] = {
|
TRACKS: Final[dict[int, dict[str, Any]]] = {
|
||||||
int(k): v for k, v in json.load(file).items()
|
int(k): v for k, v in json.load(file).items()
|
||||||
}
|
}
|
||||||
except Exception: # ignore: broad-except
|
except (FileNotFoundError, json.JSONDecodeError, OSError):
|
||||||
TRACKS = {}
|
TRACKS = {}
|
||||||
|
|
||||||
AUTOPLAY_MAP: Final[dict[str, str]] = {
|
AUTOPLAY_MAP: Final[dict[str, str]] = {
|
||||||
"0": "on",
|
"1": "Off", # display off (disabled) first
|
||||||
"1": "off",
|
"0": "Immediately",
|
||||||
"2": "5 minutes",
|
"2": "After 5 minutes",
|
||||||
"3": "10 minutes",
|
"3": "After 10 minutes",
|
||||||
"4": "30 minutes",
|
"4": "After 30 minutes",
|
||||||
"6": "1 hour",
|
"6": "After 1 hour",
|
||||||
"7": "6 hours",
|
"7": "After 6 hours",
|
||||||
"8": "12 hours",
|
"8": "After 12 hours",
|
||||||
"5": "24 hours",
|
"5": "After 24 hours", # purposefully placed so time is incrementally displayed
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR_CODE_MAP: Final[dict[int, str]] = {
|
ERROR_CODE_MAP: Final[dict[int, str]] = {
|
||||||
@@ -94,17 +94,28 @@ LED_EFFECTS: Final[dict[str, str]] = {
|
|||||||
"41": "Color Comets",
|
"41": "Color Comets",
|
||||||
}
|
}
|
||||||
|
|
||||||
STATUS_CODE_SLEEPING: Final = 6
|
STATUS_BOOTING: Final[int] = 0
|
||||||
|
STATUS_STOPPED: Final[int] = 2
|
||||||
|
STATUS_CENTERING: Final[int] = 3
|
||||||
|
STATUS_PLAYING: Final[int] = 4
|
||||||
|
STATUS_PAUSED: Final[int] = 5
|
||||||
|
STATUS_SLEEPING: Final[int] = 6
|
||||||
|
STATUS_ERROR: Final[int] = 9
|
||||||
|
STATUS_UPDATING: Final[int] = 11
|
||||||
|
STATUS_DOWNLOADING: Final[int] = 13
|
||||||
|
STATUS_BUSY: Final[int] = 14
|
||||||
|
STATUS_LIVE: Final[int] = 15
|
||||||
|
|
||||||
STATUS_CODE_MAP: Final[dict[int, str]] = {
|
STATUS_CODE_MAP: Final[dict[int, str]] = {
|
||||||
0: "booting",
|
STATUS_BOOTING: "booting",
|
||||||
2: "stopped",
|
STATUS_STOPPED: "stopped",
|
||||||
3: "centering",
|
STATUS_CENTERING: "centering",
|
||||||
4: "playing",
|
STATUS_PLAYING: "playing",
|
||||||
5: "paused",
|
STATUS_PAUSED: "paused",
|
||||||
STATUS_CODE_SLEEPING: "sleeping",
|
STATUS_SLEEPING: "sleeping",
|
||||||
9: "error",
|
STATUS_ERROR: "error",
|
||||||
11: "updating",
|
STATUS_UPDATING: "updating",
|
||||||
13: "downloading",
|
STATUS_DOWNLOADING: "downloading",
|
||||||
14: "busy",
|
STATUS_BUSY: "busy",
|
||||||
15: "live",
|
STATUS_LIVE: "live",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ from .const import (
|
|||||||
ERROR_CODE_MAP,
|
ERROR_CODE_MAP,
|
||||||
LED_EFFECTS,
|
LED_EFFECTS,
|
||||||
STATUS_CODE_MAP,
|
STATUS_CODE_MAP,
|
||||||
STATUS_CODE_SLEEPING,
|
STATUS_ERROR,
|
||||||
|
STATUS_SLEEPING,
|
||||||
TRACKS,
|
TRACKS,
|
||||||
)
|
)
|
||||||
from .utils import _bit_to_bool, _parse_int, create_svg, decrypt_svg_content
|
from .utils import _bit_to_bool, _parse_int, create_svg, decrypt_svg_content
|
||||||
@@ -163,7 +164,7 @@ class OasisDevice:
|
|||||||
Returns:
|
Returns:
|
||||||
`true` if the device is sleeping, `false` otherwise.
|
`true` if the device is sleeping, `false` otherwise.
|
||||||
"""
|
"""
|
||||||
return self.status_code == STATUS_CODE_SLEEPING
|
return self.status_code == STATUS_SLEEPING
|
||||||
|
|
||||||
def attach_client(self, client: OasisClientProtocol) -> None:
|
def attach_client(self, client: OasisClientProtocol) -> None:
|
||||||
"""Attach a transport client (MQTT, HTTP, etc.) to this device."""
|
"""Attach a transport client (MQTT, HTTP, etc.) to this device."""
|
||||||
@@ -343,7 +344,7 @@ class OasisDevice:
|
|||||||
Returns:
|
Returns:
|
||||||
str: The mapped error message when the device status indicates an error (status code 9); `None` otherwise.
|
str: The mapped error message when the device status indicates an error (status code 9); `None` otherwise.
|
||||||
"""
|
"""
|
||||||
if self.status_code == 9:
|
if self.status_code == STATUS_ERROR:
|
||||||
return ERROR_CODE_MAP.get(self.error, f"Unknown ({self.error})")
|
return ERROR_CODE_MAP.get(self.error, f"Unknown ({self.error})")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -76,15 +76,15 @@
|
|||||||
"autoplay": {
|
"autoplay": {
|
||||||
"name": "Autoplay",
|
"name": "Autoplay",
|
||||||
"state": {
|
"state": {
|
||||||
"0": "on",
|
"1": "Off",
|
||||||
"1": "off",
|
"0": "Immediately",
|
||||||
"2": "5 minutes",
|
"2": "After 5 minutes",
|
||||||
"3": "10 minutes",
|
"3": "After 10 minutes",
|
||||||
"4": "30 minutes",
|
"4": "After 30 minutes",
|
||||||
"6": "1 hour",
|
"6": "After 1 hour",
|
||||||
"7": "6 hours",
|
"7": "After 6 hours",
|
||||||
"8": "12 hours",
|
"8": "After 12 hours",
|
||||||
"5": "24 hours"
|
"5": "After 24 hours"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"playlist": {
|
"playlist": {
|
||||||
|
|||||||
@@ -76,15 +76,15 @@
|
|||||||
"autoplay": {
|
"autoplay": {
|
||||||
"name": "Autoplay",
|
"name": "Autoplay",
|
||||||
"state": {
|
"state": {
|
||||||
"0": "on",
|
"1": "Off",
|
||||||
"1": "off",
|
"0": "Immediately",
|
||||||
"2": "5 minutes",
|
"2": "After 5 minutes",
|
||||||
"3": "10 minutes",
|
"3": "After 10 minutes",
|
||||||
"4": "30 minutes",
|
"4": "After 30 minutes",
|
||||||
"6": "1 hour",
|
"6": "After 1 hour",
|
||||||
"7": "6 hours",
|
"7": "After 6 hours",
|
||||||
"8": "12 hours",
|
"8": "After 12 hours",
|
||||||
"5": "24 hours"
|
"5": "After 24 hours"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"playlist": {
|
"playlist": {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from . import OasisDeviceConfigEntry, setup_platform_from_coordinator
|
from . import OasisDeviceConfigEntry, setup_platform_from_coordinator
|
||||||
from .entity import OasisDeviceEntity
|
from .entity import OasisDeviceEntity
|
||||||
from .pyoasiscontrol import OasisDevice
|
from .pyoasiscontrol import OasisDevice
|
||||||
|
from .pyoasiscontrol.const import STATUS_UPDATING
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ class OasisDeviceUpdateEntity(OasisDeviceEntity, UpdateEntity):
|
|||||||
@property
|
@property
|
||||||
def in_progress(self) -> bool | int:
|
def in_progress(self) -> bool | int:
|
||||||
"""Update installation progress."""
|
"""Update installation progress."""
|
||||||
if self.device.status_code == 11:
|
if self.device.status_code == STATUS_UPDATING:
|
||||||
return self.device.download_progress
|
return self.device.download_progress
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user