mirror of
https://github.com/natekspencer/hacs-oasis_mini.git
synced 2025-12-06 18:44:14 -05:00
* Switch to using mqtt * Better mqtt handling when connection is interrupted * Get track info from the cloud when playlist or index changes * Add additional helpers * Dynamically handle devices and other enhancements * 📝 Add docstrings to `mqtt` Docstrings generation was requested by @natekspencer. * https://github.com/natekspencer/hacs-oasis_mini/pull/98#issuecomment-3568450288 The following files were modified: * `custom_components/oasis_mini/__init__.py` * `custom_components/oasis_mini/binary_sensor.py` * `custom_components/oasis_mini/button.py` * `custom_components/oasis_mini/config_flow.py` * `custom_components/oasis_mini/coordinator.py` * `custom_components/oasis_mini/entity.py` * `custom_components/oasis_mini/helpers.py` * `custom_components/oasis_mini/image.py` * `custom_components/oasis_mini/light.py` * `custom_components/oasis_mini/media_player.py` * `custom_components/oasis_mini/number.py` * `custom_components/oasis_mini/pyoasiscontrol/clients/cloud_client.py` * `custom_components/oasis_mini/pyoasiscontrol/clients/http_client.py` * `custom_components/oasis_mini/pyoasiscontrol/clients/mqtt_client.py` * `custom_components/oasis_mini/pyoasiscontrol/clients/transport.py` * `custom_components/oasis_mini/pyoasiscontrol/device.py` * `custom_components/oasis_mini/pyoasiscontrol/utils.py` * `custom_components/oasis_mini/select.py` * `custom_components/oasis_mini/sensor.py` * `custom_components/oasis_mini/switch.py` * `custom_components/oasis_mini/update.py` * `update_tracks.py` * Fix formatting in transport.py * Replace tabs with spaces * Use tuples instead of sets for descriptors * Encode svg in image entity * Fix iot_class * Fix tracks list url * Ensure update_tracks closes the connection * Fix number typing and docstring * Fix docstring in update_tracks * Cache playlist based on type * Fix formatting in device.py * Add missing async_send_auto_clean_command to http client * Propagate UnauthenticatedError from async_get_track_info * Adjust exceptions * Move create_client outside of try block in config_flow * Formatting * Address PR comments * Formatting * Add noqa: ARG001 on unused hass * Close cloud/MQTT clients if initial coordinator refresh fails. * Address PR again * PR fixes * Pass config entry to coordinator * Remove async_timeout (thanks ChatGPT... not) * Address PR * Replace magic numbers for status code * Update autoplay wording/ordering --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
156 lines
5.1 KiB
Python
156 lines
5.1 KiB
Python
"""Oasis device light entity."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
from typing import Any
|
|
|
|
from homeassistant.components.light import (
|
|
ATTR_BRIGHTNESS,
|
|
ATTR_EFFECT,
|
|
ATTR_RGB_COLOR,
|
|
ColorMode,
|
|
LightEntity,
|
|
LightEntityDescription,
|
|
LightEntityFeature,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
from homeassistant.util.color import (
|
|
brightness_to_value,
|
|
color_rgb_to_hex,
|
|
rgb_hex_to_rgb_list,
|
|
value_to_brightness,
|
|
)
|
|
|
|
from . import OasisDeviceConfigEntry, setup_platform_from_coordinator
|
|
from .entity import OasisDeviceEntity
|
|
from .pyoasiscontrol import OasisDevice
|
|
from .pyoasiscontrol.const import LED_EFFECTS
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant, # noqa: ARG001
|
|
entry: OasisDeviceConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up Oasis device lights using config entry."""
|
|
|
|
def make_entities(new_devices: list[OasisDevice]):
|
|
"""
|
|
Create OasisDeviceLightEntity instances for each provided Oasis device.
|
|
|
|
Parameters:
|
|
new_devices (list[OasisDevice]): Devices to wrap as light entities.
|
|
|
|
Returns:
|
|
list[OasisDeviceLightEntity]: A list of light entity instances corresponding to the input devices.
|
|
"""
|
|
return [
|
|
OasisDeviceLightEntity(entry.runtime_data, device, DESCRIPTOR)
|
|
for device in new_devices
|
|
]
|
|
|
|
setup_platform_from_coordinator(entry, async_add_entities, make_entities)
|
|
|
|
|
|
DESCRIPTOR = LightEntityDescription(key="led", translation_key="led")
|
|
|
|
|
|
class OasisDeviceLightEntity(OasisDeviceEntity, LightEntity):
|
|
"""Oasis device light entity."""
|
|
|
|
_attr_supported_features = LightEntityFeature.EFFECT
|
|
|
|
@property
|
|
def brightness(self) -> int:
|
|
"""
|
|
Get the light's brightness on a 0-255 scale.
|
|
|
|
Returns:
|
|
int: Brightness value between 0 and 255.
|
|
"""
|
|
scale = (1, self.device.brightness_max)
|
|
return value_to_brightness(scale, self.device.brightness)
|
|
|
|
@property
|
|
def color_mode(self) -> ColorMode:
|
|
"""Return the color mode of the light."""
|
|
if self.effect in (
|
|
"Rainbow",
|
|
"Glitter",
|
|
"Confetti",
|
|
"BPM",
|
|
"Juggle",
|
|
):
|
|
return ColorMode.BRIGHTNESS
|
|
return ColorMode.RGB
|
|
|
|
@property
|
|
def effect(self) -> str:
|
|
"""Return the current effect."""
|
|
return LED_EFFECTS.get(self.device.led_effect)
|
|
|
|
@property
|
|
def effect_list(self) -> list[str]:
|
|
"""Return the list of supported effects."""
|
|
return list(LED_EFFECTS.values())
|
|
|
|
@property
|
|
def is_on(self) -> bool:
|
|
"""Return True if entity is on."""
|
|
return self.device.brightness > 0
|
|
|
|
@property
|
|
def rgb_color(self) -> tuple[int, int, int] | None:
|
|
"""Return the rgb color value [int, int, int]."""
|
|
if not self.device.color:
|
|
return None
|
|
return rgb_hex_to_rgb_list(self.device.color.replace("#", ""))
|
|
|
|
@property
|
|
def supported_color_modes(self) -> set[ColorMode]:
|
|
"""Flag supported color modes."""
|
|
return {ColorMode.RGB}
|
|
|
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
|
"""Turn the entity off."""
|
|
await self.device.async_set_led(brightness=0)
|
|
|
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
|
"""
|
|
Turn the light on and set its LED state.
|
|
|
|
Processes optional keyword arguments to compute the device-specific LED parameters, then updates the device's LEDs with the resulting brightness, color, and effect.
|
|
|
|
Parameters:
|
|
kwargs: Optional control parameters recognized by the method:
|
|
ATTR_BRIGHTNESS (int): Brightness in the 0-255 Home Assistant scale. When provided,
|
|
it is converted and rounded up to the device's brightness scale (1..device.brightness_max).
|
|
When omitted, uses self.device.brightness or self.device.brightness_on.
|
|
ATTR_RGB_COLOR (tuple[int, int, int]): RGB tuple (R, G, B). When provided, it is
|
|
converted to a hex color string prefixed with '#'.
|
|
ATTR_EFFECT (str): Human-readable effect name. When provided, it is mapped to the
|
|
device's internal effect key; if no mapping exists, `None` is used.
|
|
|
|
Side effects:
|
|
Updates the underlying device LED state with the computed `brightness`, `color`, and `led_effect`.
|
|
"""
|
|
if brightness := kwargs.get(ATTR_BRIGHTNESS):
|
|
scale = (1, self.device.brightness_max)
|
|
brightness = math.ceil(brightness_to_value(scale, brightness))
|
|
else:
|
|
brightness = self.device.brightness or self.device.brightness_on
|
|
|
|
if color := kwargs.get(ATTR_RGB_COLOR):
|
|
color = f"#{color_rgb_to_hex(*color)}"
|
|
|
|
if led_effect := kwargs.get(ATTR_EFFECT):
|
|
led_effect = next(
|
|
(k for k, v in LED_EFFECTS.items() if v == led_effect), None
|
|
)
|
|
|
|
await self.device.async_set_led(
|
|
brightness=brightness, color=color, led_effect=led_effect
|
|
)
|