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" DOMAIN = "snooz"
PLATFORMS: list[Platform] = [Platform.FAN] 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 __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from datetime import timedelta
from typing import Any from typing import Any
from pysnooz.api import UnknownSnoozState from pysnooz.api import UnknownSnoozState
@ -12,16 +13,25 @@ from pysnooz.commands import (
turn_off, turn_off,
turn_on, turn_on,
) )
import voluptuous as vol
from homeassistant.components.fan import ATTR_PERCENTAGE, FanEntity, FanEntityFeature from homeassistant.components.fan import ATTR_PERCENTAGE, FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_platform
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity 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 from .models import SnoozConfigurationData
@ -30,6 +40,29 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up Snooz device from a config entry.""" """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] data: SnoozConfigurationData = hass.data[DOMAIN][entry.entry_id]
async_add_entities([SnoozFan(data)]) async_add_entities([SnoozFan(data)])
@ -108,6 +141,18 @@ class SnoozFan(FanEntity, RestoreEntity):
set_volume(percentage) if percentage > 0 else turn_off() 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: async def _async_execute_command(self, command: SnoozCommandData) -> None:
result = await self._device.async_execute_command(command) 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 import pytest
from homeassistant.components import fan 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 ( from homeassistant.const import (
ATTR_ASSUMED_STATE, ATTR_ASSUMED_STATE,
ATTR_ENTITY_ID, 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 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]) @pytest.mark.parametrize("percentage", [1, 22, 50, 99, 100])
async def test_turn_on_with_percentage( async def test_turn_on_with_percentage(
hass: HomeAssistant, snooz_fan_entity_id: str, percentage: int 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 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( async def test_push_events(
hass: HomeAssistant, mock_connected_snooz: SnoozFixture, snooz_fan_entity_id: str hass: HomeAssistant, mock_connected_snooz: SnoozFixture, snooz_fan_entity_id: str
): ):