Add services for transitioning snooz volume on or off (#83515)

* Add services for fading snooz on/off

* Rename fade to transition
This commit is contained in:
Austin Brunkhorst 2023-01-03 09:08:54 -08:00 committed by GitHub
parent 11b03b5669
commit ca7384f96e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 131 additions and 2 deletions

View File

@ -4,3 +4,11 @@ from homeassistant.const import Platform
DOMAIN = "snooz"
PLATFORMS: list[Platform] = [Platform.FAN]
SERVICE_TRANSITION_ON = "transition_on"
SERVICE_TRANSITION_OFF = "transition_off"
ATTR_VOLUME = "volume"
ATTR_DURATION = "duration"
DEFAULT_TRANSITION_DURATION = 20

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from collections.abc import Callable
from datetime import timedelta
from typing import Any
from pysnooz.api import UnknownSnoozState
@ -12,16 +13,25 @@ from pysnooz.commands import (
turn_off,
turn_on,
)
import voluptuous as vol
from homeassistant.components.fan import ATTR_PERCENTAGE, FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_platform
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from .const import DOMAIN
from .const import (
ATTR_DURATION,
ATTR_VOLUME,
DEFAULT_TRANSITION_DURATION,
DOMAIN,
SERVICE_TRANSITION_OFF,
SERVICE_TRANSITION_ON,
)
from .models import SnoozConfigurationData
@ -30,6 +40,29 @@ async def async_setup_entry(
) -> None:
"""Set up Snooz device from a config entry."""
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service(
SERVICE_TRANSITION_ON,
{
vol.Optional(ATTR_VOLUME): vol.All(
vol.Coerce(int), vol.Range(min=0, max=100)
),
vol.Optional(ATTR_DURATION, default=DEFAULT_TRANSITION_DURATION): vol.All(
vol.Coerce(int), vol.Range(min=1, max=300)
),
},
"async_transition_on",
)
platform.async_register_entity_service(
SERVICE_TRANSITION_OFF,
{
vol.Optional(ATTR_DURATION, default=DEFAULT_TRANSITION_DURATION): vol.All(
vol.Coerce(int), vol.Range(min=1, max=300)
),
},
"async_transition_off",
)
data: SnoozConfigurationData = hass.data[DOMAIN][entry.entry_id]
async_add_entities([SnoozFan(data)])
@ -108,6 +141,18 @@ class SnoozFan(FanEntity, RestoreEntity):
set_volume(percentage) if percentage > 0 else turn_off()
)
async def async_transition_on(self, duration: int, **kwargs: Any) -> None:
"""Transition on the device."""
await self._async_execute_command(
turn_on(volume=kwargs.get("volume"), duration=timedelta(seconds=duration))
)
async def async_transition_off(self, duration: int, **kwargs: Any) -> None:
"""Transition off the device."""
await self._async_execute_command(
turn_off(duration=timedelta(seconds=duration))
)
async def _async_execute_command(self, command: SnoozCommandData) -> None:
result = await self._device.async_execute_command(command)

View File

@ -0,0 +1,43 @@
transition_on:
name: Transition on
description: Transition to a target volume level over time.
target:
entity:
integration: snooz
domain: fan
fields:
duration:
name: Transition duration
description: Time it takes to reach the target volume level.
selector:
number:
min: 1
max: 300
unit_of_measurement: seconds
mode: box
volume:
name: Target volume
description: If not specified, the volume level is read from the device.
selector:
number:
min: 1
max: 100
unit_of_measurement: "%"
transition_off:
name: Transition off
description: Transition volume off over time.
target:
entity:
integration: snooz
domain: fan
fields:
duration:
name: Transition duration
description: Time it takes to turn off.
selector:
number:
min: 1
max: 300
unit_of_measurement: seconds
mode: box

View File

@ -10,7 +10,12 @@ from pysnooz.testing import MockSnoozDevice
import pytest
from homeassistant.components import fan
from homeassistant.components.snooz.const import DOMAIN
from homeassistant.components.snooz.const import (
ATTR_DURATION,
DOMAIN,
SERVICE_TRANSITION_OFF,
SERVICE_TRANSITION_ON,
)
from homeassistant.const import (
ATTR_ASSUMED_STATE,
ATTR_ENTITY_ID,
@ -41,6 +46,20 @@ async def test_turn_on(hass: HomeAssistant, snooz_fan_entity_id: str):
assert ATTR_ASSUMED_STATE not in state.attributes
async def test_transition_on(hass: HomeAssistant, snooz_fan_entity_id: str):
"""Test transitioning on the device."""
await hass.services.async_call(
DOMAIN,
SERVICE_TRANSITION_ON,
{ATTR_ENTITY_ID: [snooz_fan_entity_id], ATTR_DURATION: 1},
blocking=True,
)
state = hass.states.get(snooz_fan_entity_id)
assert state.state == STATE_ON
assert ATTR_ASSUMED_STATE not in state.attributes
@pytest.mark.parametrize("percentage", [1, 22, 50, 99, 100])
async def test_turn_on_with_percentage(
hass: HomeAssistant, snooz_fan_entity_id: str, percentage: int
@ -115,6 +134,20 @@ async def test_turn_off(hass: HomeAssistant, snooz_fan_entity_id: str):
assert ATTR_ASSUMED_STATE not in state.attributes
async def test_transition_off(hass: HomeAssistant, snooz_fan_entity_id: str):
"""Test transitioning off the device."""
await hass.services.async_call(
DOMAIN,
SERVICE_TRANSITION_OFF,
{ATTR_ENTITY_ID: [snooz_fan_entity_id], ATTR_DURATION: 1},
blocking=True,
)
state = hass.states.get(snooz_fan_entity_id)
assert state.state == STATE_OFF
assert ATTR_ASSUMED_STATE not in state.attributes
async def test_push_events(
hass: HomeAssistant, mock_connected_snooz: SnoozFixture, snooz_fan_entity_id: str
):