1
0
mirror of https://github.com/natekspencer/hacs-oasis_mini.git synced 2025-11-12 07:03:52 -05:00
This commit is contained in:
Nathan Spencer
2024-07-08 23:58:14 -06:00
parent 33faf66109
commit 10fcfb8a9f
12 changed files with 1019 additions and 56 deletions

View File

@@ -16,10 +16,12 @@ from .helpers import create_client
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
PLATFORMS = [ PLATFORMS = [
Platform.BUTTON,
Platform.IMAGE, Platform.IMAGE,
Platform.LIGHT, Platform.LIGHT,
Platform.MEDIA_PLAYER, Platform.MEDIA_PLAYER,
Platform.NUMBER, Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR, Platform.SENSOR,
Platform.SWITCH, Platform.SWITCH,
] ]
@@ -30,9 +32,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
client = create_client(entry.data | entry.options) client = create_client(entry.data | entry.options)
coordinator = OasisMiniCoordinator(hass, client) coordinator = OasisMiniCoordinator(hass, client)
await coordinator.async_config_entry_first_refresh()
try:
await coordinator.async_config_entry_first_refresh()
except Exception as ex:
_LOGGER.exception(ex)
if not coordinator.data: if not coordinator.data:
await client.session.close()
raise ConfigEntryNotReady raise ConfigEntryNotReady
hass.data[DOMAIN][entry.entry_id] = coordinator hass.data[DOMAIN][entry.entry_id] = coordinator

View File

@@ -0,0 +1,41 @@
"""Oasis Mini button entity."""
from __future__ import annotations
from typing import Any, Coroutine
from homeassistant.components.button import (
ButtonDeviceClass,
ButtonEntity,
ButtonEntityDescription,
)
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.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)])

View File

@@ -39,7 +39,7 @@ class OasisMiniCoordinator(DataUpdateCoordinator[str]):
data = await self.device.async_get_status() data = await self.device.async_get_status()
await self.device.async_get_current_track_details() await self.device.async_get_current_track_details()
except Exception as ex: except Exception as ex:
raise UpdateFailed("Couldn't read oasis_mini") from ex raise UpdateFailed("Couldn't read from the Oasis Mini") from ex
if data is None: if data is None:
raise ConfigEntryAuthFailed raise ConfigEntryAuthFailed
if data != self.data: if data != self.data:

View File

@@ -26,7 +26,7 @@ class OasisMiniEntity(CoordinatorEntity[OasisMiniCoordinator]):
entry: ConfigEntry, entry: ConfigEntry,
description: EntityDescription, description: EntityDescription,
) -> None: ) -> None:
"""Construct a Oasis Mini entity.""" """Construct an Oasis Mini entity."""
super().__init__(coordinator) super().__init__(coordinator)
self.entity_description = description self.entity_description = description
serial_number = coordinator.device.serial_number serial_number = coordinator.device.serial_number

View File

@@ -44,15 +44,14 @@ class OasisMiniLightEntity(OasisMiniEntity, LightEntity):
@property @property
def color_mode(self) -> ColorMode: def color_mode(self) -> ColorMode:
"""Return the color mode of the light.""" """Return the color mode of the light."""
# if self.effect in ( if self.effect in (
# "Rainbow", "Rainbow",
# "Glitter", "Glitter",
# "Confetti", "Confetti",
# "BPM", "BPM",
# "Juggle", "Juggle",
# "Theater", ):
# ): return ColorMode.BRIGHTNESS
# return ColorMode.BRIGHTNESS
return ColorMode.RGB return ColorMode.RGB
@property @property

View File

@@ -20,6 +20,7 @@ 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 .pyoasismini.const import TRACKS
BRIGHTNESS_SCALE = (1, 200) BRIGHTNESS_SCALE = (1, 200)
@@ -44,18 +45,18 @@ class OasisMiniMediaPlayerEntity(OasisMiniEntity, MediaPlayerEntity):
def media_duration(self) -> int: def media_duration(self) -> int:
"""Duration of current playing media in seconds.""" """Duration of current playing media in seconds."""
if ( if (
track_details := self.device._current_track_details track := self.device._current_track_details
) and "reduced_svg_content" in track_details: ) and "reduced_svg_content" in track:
return track_details["reduced_svg_content"].get("1") return track["reduced_svg_content"].get("1")
return math.ceil(self.media_position / 0.99) return math.ceil(self.media_position / 0.99)
@property @property
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 ( if not (track := self.device._current_track_details):
track_details := self.device._current_track_details track = TRACKS.get(str(self.device.current_track_id))
) and "image" in track_details: if track and "image" in track:
return f"https://app.grounded.so/uploads/{track_details['image']}" return f"https://app.grounded.so/uploads/{track['image']}"
return None return None
@property @property
@@ -71,9 +72,9 @@ class OasisMiniMediaPlayerEntity(OasisMiniEntity, MediaPlayerEntity):
@property @property
def media_title(self) -> str: def media_title(self) -> str:
"""Title of current playing media.""" """Title of current playing media."""
if track_details := self.device._current_track_details: if not (track := self.device._current_track_details):
return track_details.get("name", self.device.current_track_id) track = TRACKS.get(str(self.device.current_track_id), {})
return f"Unknown Title (#{self.device.current_track_id})" return track.get("name", f"Unknown Title (#{self.device.current_track_id})")
@property @property
def repeat(self) -> RepeatMode: def repeat(self) -> RepeatMode:
@@ -116,7 +117,8 @@ class OasisMiniMediaPlayerEntity(OasisMiniEntity, MediaPlayerEntity):
"""Send next track command.""" """Send next track command."""
if (index := self.device.playlist_index + 1) >= len(self.device.playlist): if (index := self.device.playlist_index + 1) >= len(self.device.playlist):
index = 0 index = 0
return await self.device.async_change_track(index) await self.device.async_change_track(index)
await self.coordinator.async_request_refresh()
DESCRIPTOR = MediaPlayerEntityDescription(key="oasis_mini", name=None) DESCRIPTOR = MediaPlayerEntityDescription(key="oasis_mini", name=None)

View File

@@ -1,7 +1,8 @@
"""Oasis Mini API client.""" """Oasis Mini API client."""
import asyncio
import logging import logging
from typing import Any, Callable, Final from typing import Any, Awaitable, Callable, Final
from urllib.parse import urljoin from urllib.parse import urljoin
from aiohttp import ClientSession from aiohttp import ClientSession
@@ -21,7 +22,7 @@ STATUS_CODE_MAP = {
ATTRIBUTES: Final[list[tuple[str, Callable[[str], Any]]]] = [ ATTRIBUTES: Final[list[tuple[str, Callable[[str], Any]]]] = [
("status_code", int), # see status code map ("status_code", int), # see status code map
("error", str), # error, 0 = none, and 10 = ? ("error", str), # error, 0 = none, and 10 = ?, 18 = can't download?
("ball_speed", int), # 200 - 800 ("ball_speed", int), # 200 - 800
("playlist", lambda value: [int(track) for track in value.split(",")]), # noqa: E501 # comma separated track ids ("playlist", lambda value: [int(track) for track in value.split(",")]), # noqa: E501 # comma separated track ids
("playlist_index", int), # index of above ("playlist_index", int), # index of above
@@ -163,6 +164,18 @@ class OasisMini:
"""Send play command.""" """Send play command."""
await self._async_command(params={"CMDPLAY": ""}) await self._async_command(params={"CMDPLAY": ""})
async def async_reboot(self) -> None:
"""Send reboot command."""
async def _no_response_needed(coro: Awaitable) -> None:
try:
await coro
except Exception as ex:
_LOGGER.error(ex)
reboot = self._async_command(params={"CMDBOOT": ""})
asyncio.create_task(_no_response_needed(reboot))
async def async_set_ball_speed(self, speed: int) -> None: async def async_set_ball_speed(self, speed: int) -> None:
"""Set the Oasis Mini ball speed.""" """Set the Oasis Mini ball speed."""
if not 200 <= speed <= 800: if not 200 <= speed <= 800:

View File

@@ -0,0 +1,11 @@
"""Constants."""
from __future__ import annotations
import json
import os
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)

View File

@@ -0,0 +1,827 @@
{
"131": {
"name": "A Star",
"author": "Oasis Mini",
"image": "2024/02/b90cbedf5982c44e2b88096e3f35f019.svg"
},
"358": {
"name": "Alligator",
"author": "Camila Veiga",
"image": "2024/05/83a5cb2f63a9103d9ea506cf762dee42.svg"
},
"114": {
"name": "Ant",
"author": "Camila Veiga",
"image": "2024/02/2c0494bff772e525b2888c869618b624.svg"
},
"306": {
"name": "arc flower",
"author": "mike",
"image": "2024/05/8341f09979ab20f6512d8fd88ba68b92.svg"
},
"251": {
"name": "Aries Ram",
"author": "Camila Veiga",
"image": "2024/05/02fea95ff2c9e1ef4636505a78517351.svg"
},
"246": {
"name": "Armadillo",
"author": "Oasis Mini",
"image": "2024/05/9715de0b402cd6ee7fbd3a8f44fb7404.svg"
},
"174": {
"name": "Baby Hummingbird",
"author": "Camila Veiga",
"image": "2024/02/5d982c39ad7d7613a6f43a2862fc4202.svg"
},
"359": {
"name": "BaldEagle",
"author": "Camila Veiga",
"image": "2024/05/db7781a68eaf312d15d773ed926f4719.svg"
},
"196": {
"name": "Bambi",
"author": "Camila Veiga",
"image": "2024/03/77c49931602941ff050c672257d2a4c4.svg"
},
"194": {
"name": "Bass",
"author": "Camila Veiga",
"image": "2024/03/58e1083634becb3e2e06ae294fd4abcd.svg"
},
"48": {
"name": "Beatle01",
"author": "Camila Veiga",
"image": "2024/02/6cb8369a92fcd78b7dfe67639f2568c2.svg"
},
"45": {
"name": "Beatle2",
"author": "Camila Veiga",
"image": "2024/02/34954cfa79d491552ec5d085d18662a8.svg"
},
"59": {
"name": "Beatle3",
"author": "Camila Veiga",
"image": "2024/02/d3c759d4407b4bd9dce4af2aa02fb309.svg"
},
"168": {
"name": "Betta Fish",
"author": "Oasis Mini",
"image": "2024/02/eda69bb71c0a146f59e3d7aa5af5d033.svg"
},
"102": {
"name": "Big Fish",
"author": "Camila Veiga",
"image": "2024/02/223d81730511500d47dc9ce386b54e76.svg"
},
"56": {
"name": "Branch",
"author": "Camila Veiga",
"image": "2024/02/93da7a9a8901a7ee2cbaf687c1d4f6bd.svg"
},
"133": {
"name": "Bubbles",
"author": "Oasis Mini",
"image": "2024/03/0c68af1243b823a829a83c2bced9462d.svg"
},
"349": {
"name": "Buddah",
"author": "Otávio Bittencourt",
"image": "2024/05/0e22fae64a02d4e7fe1f4ada6b1f707f.svg"
},
"257": {
"name": "Buddhist Tree",
"author": "Otávio Bittencourt",
"image": "2024/05/71c59b439b4a4f66527b045e22beacf3.svg"
},
"621": {
"name": "Bufallo",
"author": "Otávio Bittencourt",
"image": "2024/07/5fac3aff67796b4365593d38bb83dc1f.svg"
},
"157": {
"name": "Butterfly",
"author": "Oasis Mini",
"image": "2024/03/060b7c7aee2db3cc7bbf41d6f260c347.svg"
},
"58": {
"name": "Camalion",
"author": "Camila Veiga",
"image": "2024/02/f8b7ec53c2ca63f30baeacdda30659bd.svg"
},
"178": {
"name": "Cardinal Bird",
"author": "Camila Veiga",
"image": "2024/02/ba057fd71a816dd15565583cf63ee2ab.svg"
},
"215": {
"name": "Cardiod",
"author": null,
"image": "2024/03/a24da534ded92bfff8b604a630b76edd.svg"
},
"113": {
"name": "Cat Face",
"author": "Camila Veiga",
"image": "2024/02/d45a368206f87e077739e48ca73a89c6.svg"
},
"49": {
"name": "Clam",
"author": "Camila Veiga",
"image": "2024/02/25355aa8111a77ec41d1396df9123fcb.svg"
},
"505": {
"name": "Coarse Hilbert Wiper",
"author": "Xilufer",
"image": "2024/06/cb2ad632c8d1c2ca69fa9a8f544bc0c7.svg"
},
"118": {
"name": "Coarse Spiral In to Out",
"author": "Oasis Mini",
"image": "2024/02/64a1c80bbb9b5b690ee08ae11e9c0e89.svg"
},
"501": {
"name": "Coarse Spiral Out to In",
"author": "Xilufer",
"image": "2024/06/a46ab9145f30d81856ceec69ca4b8378.svg"
},
"503": {
"name": "Coarse Wipe Bottom to Top",
"author": "Xilufer",
"image": "2024/06/798a562ecda1f6ae80143ce3e69e97e2.svg"
},
"499": {
"name": "Coarse Wipe Left to Right",
"author": "Xilufer",
"image": "2024/06/335f1704e84153fa4e8334fc6e3ede6f.svg"
},
"504": {
"name": "Coarse Wipe Right to Left",
"author": "Xilufer",
"image": "2024/06/ed92b6cc7935a4c9f8a691e3308f9b49.svg"
},
"497": {
"name": "Coarse Wipe Top to Bottom",
"author": "Xilufer",
"image": "2024/06/053798917cd862f58adfc8b52310d377.svg"
},
"264": {
"name": "Crab",
"author": "Camila Veiga",
"image": "2024/05/e11995a5855afbfa05f89ce39ba65740.svg"
},
"220": {
"name": "Crane Mini",
"author": null,
"image": "2024/03/e2b3f344d6a1407d8dd5d06a2dd4d10f.svg"
},
"104": {
"name": "Cricket",
"author": "Camila Veiga",
"image": "2024/02/de8399defab8eed0f2e5de564e423c78.svg"
},
"98": {
"name": "Crocodile",
"author": "Camila Veiga",
"image": "2024/02/71b8b959f6f5320b0778f4c25a74f105.svg"
},
"68": {
"name": "Cupid",
"author": "Camila Veiga",
"image": "2024/02/8db157d5e68d132eb3766e5325936b3a.svg"
},
"261": {
"name": "Cute Cat",
"author": "Otávio Bittencourt",
"image": "2024/05/caf48ea93bc21a7391cf8aa16388f500.svg"
},
"393": {
"name": "dither_tri4",
"author": "B Perry",
"image": "2024/06/b15f38d3a4ae4f8418c769ca024bc646.svg"
},
"146": {
"name": "Dithermaster Gears",
"author": "Oasis Mini",
"image": "2024/02/92ed5ddc81f3152558a62b90f9ab99bd.svg"
},
"145": {
"name": "Dithermaster Nautilus",
"author": "Oasis Mini",
"image": "2024/02/d3e546f47a5e328320f95471e6d06e8e.svg"
},
"144": {
"name": "Dithermaster Sierpinski",
"author": "Oasis Mini",
"image": "2024/02/b873df4b7f29d81f9577b7f3d9adb649.svg"
},
"142": {
"name": "Dithermaster Sunburst",
"author": "Oasis Mini",
"image": "2024/02/560135854581fcf00007644641f317c0.svg"
},
"140": {
"name": "Dithermaster Wormhole",
"author": "Oasis Mini",
"image": "2024/02/011ba7387ca10787302da62a9ab39ce7.svg"
},
"41": {
"name": "Dog Beatle",
"author": "Camila Veiga",
"image": "2024/02/420d5b52f9c39fbec5d0301c8d1917b4.svg"
},
"36": {
"name": "Dog Golden Retriever",
"author": "Camila Veiga",
"image": "2024/02/a7419fb8058506cfc4b97a1ad44b08a1.svg"
},
"40": {
"name": "Dog Pug",
"author": "Oasis Mini",
"image": "2024/02/c2e232717568ca7fba7c835d94ff14f3.svg"
},
"162": {
"name": "Dolphin",
"author": "Oasis Mini",
"image": "2024/03/10a84a5fd019316a24e90535894db3fe.svg"
},
"244": {
"name": "Doodle Dog",
"author": "Camila Veiga",
"image": "2024/05/430de6550a4affc068160c9a4c88e226.svg"
},
"195": {
"name": "Dragon",
"author": "Camila Veiga",
"image": "2024/03/077c020cce5abf70d49fe040ddd5b209.svg"
},
"193": {
"name": "Duck",
"author": "Camila Veiga",
"image": "2024/03/22ca351799853f1452a6905a94942d4b.svg"
},
"159": {
"name": "Elephant",
"author": "Oasis Mini",
"image": "2024/03/48a449db2bbb530e5d54b54cb711ce9f.svg"
},
"129": {
"name": "Engine Turn",
"author": "Oasis Mini",
"image": "2024/02/7cb25ab3fbea0fc33a013e05bfc7b393.svg"
},
"219": {
"name": "Face",
"author": null,
"image": "2024/03/20039d6b829edcf6db73d19f9e923f2f.svg"
},
"33": {
"name": "Fibonacci Shell",
"author": "Camila Veiga",
"image": "2024/02/aaac5e59aab118064638e273ee2da27a.svg"
},
"262": {
"name": "Fish Koi",
"author": "Otávio Bittencourt",
"image": "2024/05/ce8f6c7d5e89dac56cb4296d86cd7261.svg"
},
"38": {
"name": "Flamingo",
"author": "Camila Veiga",
"image": "2024/02/cc1b007041fa87e28601c757887631fa.svg"
},
"249": {
"name": "Flower Voyage",
"author": "Camila Veiga",
"image": "2024/05/85f7a4290e6b45f76ff7653d77db3322.svg"
},
"87": {
"name": "Flowers",
"author": "Camila Veiga",
"image": "2024/02/f27ab7850ca572a4e83d9606a3528fb5.svg"
},
"241": {
"name": "French Bulldog",
"author": "Camila Veiga",
"image": "2024/05/0992b12affcc14cf541baff6b1368fd9.svg"
},
"60": {
"name": "Frog",
"author": "Camila Veiga",
"image": "2024/02/1d37a8cd59f9222670949681f607f454.svg"
},
"252": {
"name": "Furry Moth",
"author": "Camila Veiga",
"image": "2024/05/fec69cc408643e629247c56648df6dee.svg"
},
"88": {
"name": "Geometric Hummingbird",
"author": "Camila Veiga",
"image": "2024/02/f2ddf3bd2d74b7674832d7ace5e54992.svg"
},
"81": {
"name": "Geometric Wolf",
"author": "Camila Veiga",
"image": "2024/02/a8cc546c3d9dfd1ade921817299a626a.svg"
},
"332": {
"name": "Giant Octopus",
"author": "Otávio Bittencourt",
"image": "2024/05/862ce4ee7aaba8b3832d136a9909c15d.svg"
},
"224": {
"name": "Happy Easter",
"author": "Oasis Mini",
"image": "2024/03/c0dfc0175a06768d06ef4a8863ddb5c6.svg"
},
"581": {
"name": "Happy4th",
"author": "zach8644",
"image": "2024/07/21574747a7892b04931bdd5135175d04.svg"
},
"356": {
"name": "Hedgehog",
"author": "Camila Veiga",
"image": "2024/05/cabfd2aa2b691af8db0d95bdfe0fd32e.svg"
},
"147": {
"name": "Hilbert",
"author": "Oasis Mini",
"image": "2024/03/18d8fab24afbacae8743b154ede27ac0.svg"
},
"496": {
"name": "Hilbert Wiper",
"author": "Xilufer",
"image": "2024/06/3ed2bf50e3aabdbc4f5de7d81c46fdeb.svg"
},
"192": {
"name": "Hippo",
"author": "Camila Veiga",
"image": "2024/03/cef030f36e1d9ee172603ca2ebf52045.svg"
},
"213": {
"name": "Honeybee",
"author": null,
"image": "2024/03/916fa92c31245f887bf6842dc0abf087.svg"
},
"100": {
"name": "Hummingbird",
"author": "Camila Veiga",
"image": "2024/02/6df68af94360e823a2888925fc935da4.svg"
},
"304": {
"name": "Iguana",
"author": "Otávio Bittencourt",
"image": "2024/05/24a538188acf7ae153746aff00e35743.svg"
},
"72": {
"name": "Iguana",
"author": "Camila Veiga",
"image": "2024/02/6a9c6db932fc00eacdde436bfad6affd.svg"
},
"139": {
"name": "Intersection",
"author": "Oasis Mini",
"image": "2024/02/a8e3c5d676faa430c0d6818ebff8044c.svg"
},
"238": {
"name": "Jack Russell Terrier",
"author": "Camila Veiga",
"image": "2024/05/d000c4bb896280d455dd6ac53ed8aa48.svg"
},
"170": {
"name": "Jellyfish",
"author": "Oasis Mini",
"image": "2024/03/2be69280b76eef7323d8f107afb6d142.svg"
},
"189": {
"name": "Kakapo Parrot Bird",
"author": "Camila Veiga",
"image": "2024/02/3482a2aafe5facaafff2b83531910512.svg"
},
"239": {
"name": "Kobra",
"author": "Camila Veiga",
"image": "2024/05/af42684b3f1ad0cc1926d28f6add3dec.svg"
},
"240": {
"name": "Labrador Retriever",
"author": "Camila Veiga",
"image": "2024/05/9241d0be1d61fa2b37681cb5d90d15be.svg"
},
"173": {
"name": "Light Bulb",
"author": "Oasis Mini",
"image": "2024/03/431d42927eca6b93a33c520a495d621d.svg"
},
"121": {
"name": "Line Wiper",
"author": "Zach",
"image": "2024/02/b406f9245e23ded2e3a781ccc5e5ca1f.svg"
},
"385": {
"name": "Lion",
"author": "Otávio Bittencourt",
"image": "2024/06/56ace3527391978ce17b65fc14f69ed3.svg"
},
"300": {
"name": "Little Heart",
"author": "Evan",
"image": "2024/05/8c68933d4b7e07ad9dc3496f7b82f106.svg"
},
"177": {
"name": "Lone Blue Jay Bird",
"author": "Camila Veiga",
"image": "2024/02/4c5b69c5fe436c8cbb5df697202dcaa5.svg"
},
"250": {
"name": "Long Tail Moth",
"author": "Camila Veiga",
"image": "2024/05/23c032dfc886d10c43b60fee7d1a5c92.svg"
},
"128": {
"name": "Loops",
"author": "Oasis Mini",
"image": "2024/02/5527235b74c3f9327728caddf73eda5b.svg"
},
"188": {
"name": "Macaw",
"author": "Camila Veiga",
"image": "2024/02/f36f92f355cbfc0bd1cfc779ec64d8fb.svg"
},
"64": {
"name": "Mandala",
"author": "Camila Veiga",
"image": "2024/02/21eb184da4fe1eeefdd7c220f209d3f1.svg"
},
"339": {
"name": "Marmoset Monkey",
"author": "Otávio Bittencourt",
"image": "2024/05/344128d7d7e468db26af2f04b2d7d088.svg"
},
"212": {
"name": "Medusa",
"author": null,
"image": "2024/03/5b8954e0d62998cdfd9fccbc8b63173e.svg"
},
"78": {
"name": "Mini Bouquet",
"author": "Camila Veiga",
"image": "2024/02/42a10229d228504945cde2dcab34e145.svg"
},
"179": {
"name": "Monkey",
"author": "Camila Veiga",
"image": "2024/02/8f3b78fecee6f47ea568c6a580a9a2fc.svg"
},
"155": {
"name": "Monstera",
"author": "Oasis Mini",
"image": "2024/02/c2b76034445415a1327cafe24781a2a8.svg"
},
"202": {
"name": "Moth",
"author": "Camila Veiga",
"image": "2024/03/374e12126f1618ee960b44a23c3229ca.svg"
},
"63": {
"name": "Mushroom",
"author": "Camila Veiga",
"image": "2024/02/63f18a5f611c9b798178110c885b7b7b.svg"
},
"101": {
"name": "Mushroom Forest",
"author": "Camila Veiga",
"image": "2024/02/df973d6848a4173b54ac6666847798d1.svg"
},
"138": {
"name": "Noise Curves",
"author": "Oasis Mini",
"image": "2024/03/24583f60b82a4198db5ba5c922aaa9da.svg"
},
"150": {
"name": "Noise Waves",
"author": "Oasis Mini",
"image": "2024/03/ef39021e727be220dab8962ff9077aca.svg"
},
"171": {
"name": "Octopus",
"author": "Camila Veiga",
"image": "2024/03/761741f1eabae8b3183e48e3a367fcfe.svg"
},
"431": {
"name": "Otter",
"author": "Otávio Bittencourt",
"image": "2024/06/af229556334619038aa62f913e36d455.svg"
},
"37": {
"name": "Owl",
"author": "Camila Veiga",
"image": "2024/02/eb45cee22c24225da3a79abf6f907765.svg"
},
"221": {
"name": "Pattern 3",
"author": null,
"image": "2024/03/419f74b031a6ea0cfd794985bb983960.svg"
},
"350": {
"name": "Pelican",
"author": "Otávio Bittencourt",
"image": "2024/05/678ca8eed19618dade7d4ed00e3ebdd9.svg"
},
"24": {
"name": "Penguin",
"author": "Camila Veiga",
"image": "2024/03/f3a718de2ff3fd37148fd16967113f87.svg"
},
"137": {
"name": "Pinwheel",
"author": "Oasis Mini",
"image": "2024/02/554b6e96961ce9c9459eaf9826c09c9a.svg"
},
"243": {
"name": "Pitbull",
"author": "Camila Veiga",
"image": "2024/05/6aa2e109b8f3eb068288bdbf652297a3.svg"
},
"103": {
"name": "Rabbit",
"author": "Camila Veiga",
"image": "2024/02/409d178f619d5b1dad43f34c380a8768.svg"
},
"211": {
"name": "Rabbit",
"author": null,
"image": "2024/03/7501a028530482e89bb726e425a6a8cb.svg"
},
"210": {
"name": "Rocket",
"author": null,
"image": "2024/03/7a60d9b004f546948fde90489e19f22a.svg"
},
"105": {
"name": "Rooster",
"author": "Camila Veiga",
"image": "2024/02/458f026c21efdaf85dd9483515c793ff.svg"
},
"156": {
"name": "Rose",
"author": "Oasis Mini",
"image": "2024/03/8a5ff9792afe8da301350dee4a8e4278.svg"
},
"123": {
"name": "Sawtooth",
"author": "Oasis Mini",
"image": "2024/02/4fe77ed20684244e6c83784f199c752e.svg"
},
"197": {
"name": "Scorpion",
"author": "Camila Veiga",
"image": "2024/03/d3e71b4963a7d61d55be2760482890aa.svg"
},
"345": {
"name": "Sea Horse",
"author": "Otávio Bittencourt",
"image": "2024/05/6de639607e4bbdca8f8ecb57c402cd6e.svg"
},
"172": {
"name": "Seahorse",
"author": "Oasis Mini",
"image": "2024/03/2283d765860cddd72025a150921dd6ce.svg"
},
"190": {
"name": "Seahorse",
"author": "Camila Veiga",
"image": "2024/03/c3ec7121261a3f75ff56630b672136fe.svg"
},
"357": {
"name": "Seal",
"author": "Camila Veiga",
"image": "2024/05/4833cf1cfbfc79ef6195bd2e1c006059.svg"
},
"390": {
"name": "Sheep",
"author": "Otávio Bittencourt",
"image": "2024/06/31e46f5f5997e742394892849eda505a.svg"
},
"136": {
"name": "Shield",
"author": "Oasis Mini",
"image": "2024/02/6f0952def38040c7a48bc56c7c44bf67.svg"
},
"203": {
"name": "Shimeji",
"author": "Camila Veiga",
"image": "2024/03/9873bcedd0b8f0560d8619fdddf42090.svg"
},
"149": {
"name": "Sierpenski",
"author": "Oasis Mini",
"image": "2024/03/27205730092d3b5c866bd53b9d26be97.svg"
},
"209": {
"name": "Skull",
"author": null,
"image": "2024/03/374f2efbfed6e4ab91137dbc6068e446.svg"
},
"158": {
"name": "Slightly Frightening Panda",
"author": "Oasis Mini",
"image": "2024/03/6877ff4d26904605066f246f31ed3cea.svg"
},
"180": {
"name": "Slot",
"author": "Camila Veiga",
"image": "2024/02/d4999457ab2769ddaef2c75736adab3a.svg"
},
"266": {
"name": "Snail",
"author": "Camila Veiga",
"image": "2024/05/6dd0ce7a83776d0a275d4bfcdc37d53f.svg"
},
"160": {
"name": "Spaceman",
"author": "Oasis Mini",
"image": "2024/03/b3d10e661d26c0bd7f8cc3ecee3b0ace.svg"
},
"125": {
"name": "Spiral Gyrations",
"author": "Oasis Mini",
"image": "2024/02/bfe3669fb18b99ba153bb07c2ea1d223.svg"
},
"119": {
"name": "Spiral In to Out",
"author": "Oasis Mini",
"image": "2024/02/f52427297697620a11131d037078fa2e.svg"
},
"117": {
"name": "Spiral Out to In",
"author": "Oasis Mini",
"image": "2024/03/4402aad108bb2a5c100b9f150ea3d97b.svg"
},
"20": {
"name": "SpiralizedWeb",
"author": "Zach",
"image": "2024/02/99e4863256d8ffb5f3b5239f19e2270b.svg"
},
"126": {
"name": "Spun Web",
"author": "Zach",
"image": "2024/02/99e4863256d8ffb5f3b5239f19e2270b.svg"
},
"267": {
"name": "Squid",
"author": "Camila Veiga",
"image": "2024/05/948f8d6eb814ce3a21e5a76ed90b3ea4.svg"
},
"175": {
"name": "Squirrel",
"author": "Camila Veiga",
"image": "2024/05/57981d2262e861b1b36ee842cff8b0d8.svg"
},
"265": {
"name": "Starfish",
"author": "Camila Veiga",
"image": "2024/05/a05aa52a44f34da15ddb1f5a3009fc1d.svg"
},
"115": {
"name": "String ray",
"author": "Camila Veiga",
"image": "2024/02/ca0d18b5213d4a18e0fabe76d4170247.svg"
},
"245": {
"name": "Sunflower",
"author": "Camila Veiga",
"image": "2024/05/bf6e4b6d739226139ced981ba9e38f60.svg"
},
"208": {
"name": "Swallow",
"author": null,
"image": "2024/03/026f52b5e539f1dd14215c751923024e.svg"
},
"370": {
"name": "Swirl",
"author": "Matt",
"image": "2024/05/156a6da37221c44878cd3c155f1d6918.svg"
},
"161": {
"name": "T-Rex",
"author": "Oasis Mini",
"image": "2024/03/3829ea91a3af828e7046f473707b0627.svg"
},
"455": {
"name": "Teste",
"author": "Otávio Bittencourt",
"image": "2024/06/ecd77e23fe859ba8e7e8c6a6ecfc9b8e.svg"
},
"223": {
"name": "The Knot",
"author": null,
"image": "2024/03/63013ee4146acb3af028949f98944bd9.svg"
},
"308": {
"name": "The Noise",
"author": "Matt",
"image": "2024/05/765c11e5dda140b236075b912731f69f.svg"
},
"483": {
"name": "Tiger",
"author": "Otávio Bittencourt",
"image": "2024/06/1bfb7dcda755b2d98ee85b83748d095b.svg"
},
"237": {
"name": "Toy Poodle",
"author": "Camila Veiga",
"image": "2024/05/9f559796eac7691049af8dadda742ad8.svg"
},
"130": {
"name": "Tri-Circle",
"author": "Oasis Mini",
"image": "2024/02/0a41c8694c1cd6559baafd82963286f6.svg"
},
"135": {
"name": "Triforce",
"author": "Oasis Mini",
"image": "2024/02/fefeea07184b4597243ba7b2dd2711fa.svg"
},
"247": {
"name": "Tropical Frog",
"author": "Oasis Mini",
"image": "2024/05/24f51e96925b83d64c8f63bd6c1b36b4.svg"
},
"242": {
"name": "Tropical Monkey texture",
"author": "Camila Veiga",
"image": "2024/05/6358af0a11dfa985f61bd9a7dec90fd3.svg"
},
"248": {
"name": "Tropical Snake",
"author": "Camila Veiga",
"image": "2024/05/d5bf2ba5d6417196d106b4da756035a1.svg"
},
"54": {
"name": "Tucan",
"author": "Camila Veiga",
"image": "2024/02/699dd2fff292f1104f8dbdf60f187043.svg"
},
"176": {
"name": "Tulips",
"author": "Camila Veiga",
"image": "2024/02/876563c5bafafee7e31c7ed96a846e00.svg"
},
"120": {
"name": "Turtle",
"author": "Junior Veloso",
"image": "2024/02/0dde4cf30929697c9d9145145771db31.svg"
},
"218": {
"name": "Unicorn",
"author": null,
"image": "2024/03/ed353a6e18917d9c2df0e4278e59b01d.svg"
},
"124": {
"name": "Warped Reuleaux",
"author": "Oasis Mini",
"image": "2024/02/a2aa2e71910c96680f78b65b81201b61.svg"
},
"127": {
"name": "Warped Squares",
"author": "Oasis Mini",
"image": "2024/02/8042b0f37b0cb37c739ac64e754ab774.svg"
},
"169": {
"name": "Whale",
"author": "Oasis Mini",
"image": "2024/03/283e1c9b6ee397a7822c58af01fcbbc3.svg"
},
"287": {
"name": "Windmill",
"author": "Matt",
"image": "2024/05/bcad3d06339ec7a345420191b7201ce1.svg"
},
"500": {
"name": "Wipe Left to Right",
"author": "Xilufer",
"image": "2024/06/3bdc415360a6466cf6245527bf85bd29.svg"
},
"498": {
"name": "Wipe Top to Bottom",
"author": "Xilufer",
"image": "2024/06/56b0cb09f15b44bac418ee2d1ed1940e.svg"
},
"77": {
"name": "Wolf head",
"author": "Camila Veiga",
"image": "2024/02/0c35befdb13ab7702f4c3b71371bf75c.svg"
},
"360": {
"name": "Woodpecker",
"author": "Camila Veiga",
"image": "2024/05/95ea026589751d7fca381f2c3df9380d.svg"
},
"437": {
"name": "Yorkshire",
"author": "Otávio Bittencourt",
"image": "2024/06/be59f584c87cfff3aa13e5887a69e183.svg"
}
}

View File

@@ -4,16 +4,15 @@ import logging
import math import math
from xml.etree.ElementTree import Element, SubElement, tostring from xml.etree.ElementTree import Element, SubElement, tostring
# import re
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
COLOR_DARK = "#28292E"
COLOR_LIGHT = "#FFFFFF" BACKGROUND_FILL = ("#CCC9C4", "#28292E")
COLOR_LIGHT_SHADE = "#FFFFFF" COLOR_DARK = ("#28292E", "#F4F5F8")
COLOR_MEDIUM_SHADE = "#E5E2DE" COLOR_LIGHT = ("#FFFFFF", "#222428")
COLOR_MEDIUM_TINT = "#B8B8B8" COLOR_LIGHT_SHADE = ("#FFFFFF", "#86888F")
FILL_SVG_STATUS = "#CCC9C4" COLOR_MEDIUM_SHADE = ("#E5E2DE", "#86888F")
COLOR_MEDIUM_TINT = ("#B8B8B8", "#FFFFFF")
def _bit_to_bool(val: str) -> bool: def _bit_to_bool(val: str) -> bool:
@@ -27,7 +26,6 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
try: try:
if progress is not None: if progress is not None:
paths = svg_content.split("L") paths = svg_content.split("L")
# paths=re.findall('([a-zA-Z][^a-zA-Z]+)',svg_content)
total = track.get("reduced_svg_content", {}).get(model_id, len(paths)) total = track.get("reduced_svg_content", {}).get(model_id, len(paths))
percent = (100 * progress) / total percent = (100 * progress) / total
progress = math.floor((percent / 100) * len(paths)) progress = math.floor((percent / 100) * len(paths))
@@ -42,15 +40,24 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
"class": "svg-status", "class": "svg-status",
}, },
) )
# style = SubElement(svg, "style")
# style.text = """ style = SubElement(svg, "style")
# .progress_arc_incomplete { style.text = f"""
# stroke: #E5E2DE; circle.background {{ fill: {BACKGROUND_FILL[0]}; }}
# } circle.ball {{ stroke: {COLOR_DARK[0]}; fill: {COLOR_LIGHT[0]}; }}
# circle.circleClass { path.progress_arc {{ stroke: {COLOR_MEDIUM_SHADE[0]}; }}
# stroke: #006600; path.progress_arc_complete {{ stroke: {COLOR_DARK[0]}; }}
# fill: #cc0000; path.track {{ stroke: {COLOR_LIGHT_SHADE[0]}; }}
# }""" path.track_complete {{ stroke: {COLOR_MEDIUM_TINT[0]}; }}
@media (prefers-color-scheme: dark) {{
circle.background {{ fill: {BACKGROUND_FILL[1]}; }}
circle.ball {{ stroke: {COLOR_DARK[1]}; fill: {COLOR_LIGHT[1]}; }}
path.progress_arc {{ stroke: {COLOR_MEDIUM_SHADE[1]}; }}
path.progress_arc_complete {{ stroke: {COLOR_DARK[1]}; }}
path.track {{ stroke: {COLOR_LIGHT_SHADE[1]}; }}
path.track_complete {{ stroke: {COLOR_MEDIUM_TINT[1]}; }}
}}"""
group = SubElement( group = SubElement(
svg, svg,
"g", "g",
@@ -63,8 +70,7 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
group, group,
"path", "path",
{ {
"class": "progress_arc_incomplete", "class": "progress_arc",
"stroke": COLOR_MEDIUM_SHADE,
"stroke-width": "2", "stroke-width": "2",
"d": progress_arc, "d": progress_arc,
}, },
@@ -76,7 +82,7 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
group, group,
"path", "path",
{ {
"stroke": COLOR_DARK, "class": "progress_arc_complete",
"stroke-width": "4", "stroke-width": "4",
"d": "L".join(progress_arc_paths[:paths_to_draw]), "d": "L".join(progress_arc_paths[:paths_to_draw]),
}, },
@@ -86,8 +92,8 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
group, group,
"circle", "circle",
{ {
"class": "background",
"r": "100", "r": "100",
"fill": FILL_SVG_STATUS,
"cx": "100", "cx": "100",
"cy": "100", "cy": "100",
"opacity": "0.3", "opacity": "0.3",
@@ -98,7 +104,7 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
group, group,
"path", "path",
{ {
"stroke": COLOR_LIGHT_SHADE, "class": "track",
"stroke-width": "1.4", "stroke-width": "1.4",
"d": svg_content, "d": svg_content,
}, },
@@ -108,7 +114,7 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
group, group,
"path", "path",
{ {
"stroke": COLOR_MEDIUM_TINT, "class": "track_complete",
"stroke-width": "1.8", "stroke-width": "1.8",
"d": "L".join(paths[:progress]), "d": "L".join(paths[:progress]),
}, },
@@ -119,9 +125,8 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
group, group,
"circle", "circle",
{ {
"stroke": COLOR_DARK, "class": "ball",
"stroke-width": "1", "stroke-width": "1",
"fill": COLOR_LIGHT,
"cx": f"{_cx:.2f}", "cx": f"{_cx:.2f}",
"cy": f"{_cy:.2f}", "cy": f"{_cy:.2f}",
"r": "5", "r": "5",
@@ -131,3 +136,4 @@ def draw_svg(track: dict, progress: int, model_id: str) -> str | None:
return tostring(svg).decode() return tostring(svg).decode()
except Exception as e: except Exception as e:
_LOGGER.exception(e) _LOGGER.exception(e)
return None

View File

@@ -0,0 +1,62 @@
"""Oasis Mini select entity."""
from __future__ import annotations
from typing import Any
from homeassistant.components.select import SelectEntity, SelectEntityDescription
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.const import TRACKS
class OasisMiniSelectEntity(OasisMiniEntity, SelectEntity):
"""Oasis Mini select entity."""
def __init__(
self,
coordinator: OasisMiniCoordinator,
entry: ConfigEntry[Any],
description: EntityDescription,
) -> None:
"""Construct an Oasis Mini select entity."""
super().__init__(coordinator, entry, description)
self._attr_options = [
TRACKS.get(str(track), {}).get("name", str(track))
for track in self.device.playlist
]
@property
def current_option(self) -> str:
"""Return the selected entity option to represent the entity state."""
return self.options[self.device.playlist_index]
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
await self.device.async_change_track(self.options.index(option))
await self.coordinator.async_request_refresh()
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._attr_options = [
TRACKS.get(str(track), {}).get("name", str(track))
for track in self.device.playlist
]
return super()._handle_coordinator_update()
DESCRIPTOR = SelectEntityDescription(key="playlist", name="Playlist")
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up Oasis Mini select using config entry."""
coordinator: OasisMiniCoordinator = hass.data[DOMAIN][entry.entry_id]
async_add_entities([OasisMiniSelectEntity(coordinator, entry, DESCRIPTOR)])

View File

@@ -49,11 +49,6 @@ DESCRIPTORS = {
name="Download progress", name="Download progress",
state_class=SensorStateClass.TOTAL_INCREASING, state_class=SensorStateClass.TOTAL_INCREASING,
), ),
OasisMiniSensorEntityDescription(
key="playlist",
name="Playlist",
lookup_fn=lambda device: ",".join(map(str, device.playlist)),
),
} }
OTHERS = { OTHERS = {