parent
91668e99e3
commit
746d1800f9
|
@ -25,6 +25,7 @@ import voluptuous as vol
|
|||
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_MODE,
|
||||
CONF_PASSWORD,
|
||||
CONF_SCAN_INTERVAL,
|
||||
CONF_USERNAME,
|
||||
|
@ -40,11 +41,10 @@ from homeassistant.helpers.typing import ConfigType
|
|||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
from .const import (
|
||||
ATTR_DURATION_DAYS,
|
||||
ATTR_DURATION_HOURS,
|
||||
ATTR_DURATION,
|
||||
ATTR_DURATION_UNTIL,
|
||||
ATTR_SYSTEM_MODE,
|
||||
ATTR_ZONE_TEMP,
|
||||
ATTR_PERIOD,
|
||||
ATTR_SETPOINT,
|
||||
CONF_LOCATION_IDX,
|
||||
DOMAIN,
|
||||
SCAN_INTERVAL_DEFAULT,
|
||||
|
@ -81,7 +81,7 @@ RESET_ZONE_OVERRIDE_SCHEMA: Final = vol.Schema(
|
|||
SET_ZONE_OVERRIDE_SCHEMA: Final = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
|
||||
vol.Required(ATTR_ZONE_TEMP): vol.All(
|
||||
vol.Required(ATTR_SETPOINT): vol.All(
|
||||
vol.Coerce(float), vol.Range(min=4.0, max=35.0)
|
||||
),
|
||||
vol.Optional(ATTR_DURATION_UNTIL): vol.All(
|
||||
|
@ -222,7 +222,7 @@ def setup_service_functions(
|
|||
# Permanent-only modes will use this schema
|
||||
perm_modes = [m[SZ_SYSTEM_MODE] for m in modes if not m[SZ_CAN_BE_TEMPORARY]]
|
||||
if perm_modes: # any of: "Auto", "HeatingOff": permanent only
|
||||
schema = vol.Schema({vol.Required(ATTR_SYSTEM_MODE): vol.In(perm_modes)})
|
||||
schema = vol.Schema({vol.Required(ATTR_MODE): vol.In(perm_modes)})
|
||||
system_mode_schemas.append(schema)
|
||||
|
||||
modes = [m for m in modes if m[SZ_CAN_BE_TEMPORARY]]
|
||||
|
@ -232,8 +232,8 @@ def setup_service_functions(
|
|||
if temp_modes: # any of: "AutoWithEco", permanent or for 0-24 hours
|
||||
schema = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_SYSTEM_MODE): vol.In(temp_modes),
|
||||
vol.Optional(ATTR_DURATION_HOURS): vol.All(
|
||||
vol.Required(ATTR_MODE): vol.In(temp_modes),
|
||||
vol.Optional(ATTR_DURATION): vol.All(
|
||||
cv.time_period,
|
||||
vol.Range(min=timedelta(hours=0), max=timedelta(hours=24)),
|
||||
),
|
||||
|
@ -246,8 +246,8 @@ def setup_service_functions(
|
|||
if temp_modes: # any of: "Away", "Custom", "DayOff", permanent or for 1-99 days
|
||||
schema = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_SYSTEM_MODE): vol.In(temp_modes),
|
||||
vol.Optional(ATTR_DURATION_DAYS): vol.All(
|
||||
vol.Required(ATTR_MODE): vol.In(temp_modes),
|
||||
vol.Optional(ATTR_PERIOD): vol.All(
|
||||
cv.time_period,
|
||||
vol.Range(min=timedelta(days=1), max=timedelta(days=99)),
|
||||
),
|
||||
|
|
|
@ -29,7 +29,7 @@ from homeassistant.components.climate import (
|
|||
ClimateEntityFeature,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.const import PRECISION_TENTHS, UnitOfTemperature
|
||||
from homeassistant.const import ATTR_MODE, PRECISION_TENTHS, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
@ -38,11 +38,10 @@ from homeassistant.util import dt as dt_util
|
|||
|
||||
from . import EVOHOME_KEY
|
||||
from .const import (
|
||||
ATTR_DURATION_DAYS,
|
||||
ATTR_DURATION_HOURS,
|
||||
ATTR_DURATION,
|
||||
ATTR_DURATION_UNTIL,
|
||||
ATTR_SYSTEM_MODE,
|
||||
ATTR_ZONE_TEMP,
|
||||
ATTR_PERIOD,
|
||||
ATTR_SETPOINT,
|
||||
EvoService,
|
||||
)
|
||||
from .coordinator import EvoDataUpdateCoordinator
|
||||
|
@ -180,7 +179,7 @@ class EvoZone(EvoChild, EvoClimateEntity):
|
|||
return
|
||||
|
||||
# otherwise it is EvoService.SET_ZONE_OVERRIDE
|
||||
temperature = max(min(data[ATTR_ZONE_TEMP], self.max_temp), self.min_temp)
|
||||
temperature = max(min(data[ATTR_SETPOINT], self.max_temp), self.min_temp)
|
||||
|
||||
if ATTR_DURATION_UNTIL in data:
|
||||
duration: timedelta = data[ATTR_DURATION_UNTIL]
|
||||
|
@ -349,16 +348,16 @@ class EvoController(EvoClimateEntity):
|
|||
Data validation is not required, it will have been done upstream.
|
||||
"""
|
||||
if service == EvoService.SET_SYSTEM_MODE:
|
||||
mode = data[ATTR_SYSTEM_MODE]
|
||||
mode = data[ATTR_MODE]
|
||||
else: # otherwise it is EvoService.RESET_SYSTEM
|
||||
mode = EvoSystemMode.AUTO_WITH_RESET
|
||||
|
||||
if ATTR_DURATION_DAYS in data:
|
||||
if ATTR_PERIOD in data:
|
||||
until = dt_util.start_of_local_day()
|
||||
until += data[ATTR_DURATION_DAYS]
|
||||
until += data[ATTR_PERIOD]
|
||||
|
||||
elif ATTR_DURATION_HOURS in data:
|
||||
until = dt_util.now() + data[ATTR_DURATION_HOURS]
|
||||
elif ATTR_DURATION in data:
|
||||
until = dt_util.now() + data[ATTR_DURATION]
|
||||
|
||||
else:
|
||||
until = None
|
||||
|
|
|
@ -18,11 +18,10 @@ USER_DATA: Final = "user_data"
|
|||
SCAN_INTERVAL_DEFAULT: Final = timedelta(seconds=300)
|
||||
SCAN_INTERVAL_MINIMUM: Final = timedelta(seconds=60)
|
||||
|
||||
ATTR_SYSTEM_MODE: Final = "mode"
|
||||
ATTR_DURATION_DAYS: Final = "period"
|
||||
ATTR_DURATION_HOURS: Final = "duration"
|
||||
ATTR_PERIOD: Final = "period" # number of days
|
||||
ATTR_DURATION: Final = "duration" # number of minutes, <24h
|
||||
|
||||
ATTR_ZONE_TEMP: Final = "setpoint"
|
||||
ATTR_SETPOINT: Final = "setpoint"
|
||||
ATTR_DURATION_UNTIL: Final = "duration"
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
"""The tests for the native services of Evohome."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from evohomeasync2 import EvohomeClient
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.evohome.const import (
|
||||
ATTR_DURATION,
|
||||
ATTR_PERIOD,
|
||||
ATTR_SETPOINT,
|
||||
DOMAIN,
|
||||
EvoService,
|
||||
)
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_MODE
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
@pytest.mark.parametrize("install", ["default"])
|
||||
async def test_service_refresh_system(
|
||||
hass: HomeAssistant,
|
||||
evohome: EvohomeClient,
|
||||
) -> None:
|
||||
"""Test Evohome's refresh_system service (for all temperature control systems)."""
|
||||
|
||||
# EvoService.REFRESH_SYSTEM
|
||||
with patch("evohomeasync2.location.Location.update") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.REFRESH_SYSTEM,
|
||||
{},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("install", ["default"])
|
||||
async def test_service_reset_system(
|
||||
hass: HomeAssistant,
|
||||
ctl_id: str,
|
||||
) -> None:
|
||||
"""Test Evohome's reset_system service (for a temperature control system)."""
|
||||
|
||||
# EvoService.RESET_SYSTEM (if SZ_AUTO_WITH_RESET in modes)
|
||||
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.RESET_SYSTEM,
|
||||
{},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with("AutoWithReset", until=None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("install", ["default"])
|
||||
async def test_ctl_set_system_mode(
|
||||
hass: HomeAssistant,
|
||||
ctl_id: str,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test Evohome's set_system_mode service (for a temperature control system)."""
|
||||
|
||||
# EvoService.SET_SYSTEM_MODE: Auto
|
||||
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.SET_SYSTEM_MODE,
|
||||
{
|
||||
ATTR_MODE: "Auto",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with("Auto", until=None)
|
||||
|
||||
freezer.move_to("2024-07-10T12:00:00+00:00")
|
||||
|
||||
# EvoService.SET_SYSTEM_MODE: AutoWithEco, hours=12
|
||||
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.SET_SYSTEM_MODE,
|
||||
{
|
||||
ATTR_MODE: "AutoWithEco",
|
||||
ATTR_DURATION: {"hours": 12},
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with(
|
||||
"AutoWithEco", until=datetime(2024, 7, 11, 0, 0, tzinfo=UTC)
|
||||
)
|
||||
|
||||
# EvoService.SET_SYSTEM_MODE: Away, days=7
|
||||
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.SET_SYSTEM_MODE,
|
||||
{
|
||||
ATTR_MODE: "Away",
|
||||
ATTR_PERIOD: {"days": 7},
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with(
|
||||
"Away", until=datetime(2024, 7, 16, 23, 0, tzinfo=UTC)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("install", ["default"])
|
||||
async def test_zone_clear_zone_override(
|
||||
hass: HomeAssistant,
|
||||
zone_id: str,
|
||||
) -> None:
|
||||
"""Test Evohome's clear_zone_override service (for a heating zone)."""
|
||||
|
||||
# EvoZoneMode.FOLLOW_SCHEDULE
|
||||
with patch("evohomeasync2.zone.Zone.reset") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.RESET_ZONE_OVERRIDE,
|
||||
{
|
||||
ATTR_ENTITY_ID: zone_id,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("install", ["default"])
|
||||
async def test_zone_set_zone_override(
|
||||
hass: HomeAssistant,
|
||||
zone_id: str,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test Evohome's set_zone_override service (for a heating zone)."""
|
||||
|
||||
freezer.move_to("2024-07-10T12:00:00+00:00")
|
||||
|
||||
# EvoZoneMode.PERMANENT_OVERRIDE
|
||||
with patch("evohomeasync2.zone.Zone.set_temperature") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.SET_ZONE_OVERRIDE,
|
||||
{
|
||||
ATTR_ENTITY_ID: zone_id,
|
||||
ATTR_SETPOINT: 19.5,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with(19.5, until=None)
|
||||
|
||||
# EvoZoneMode.TEMPORARY_OVERRIDE
|
||||
with patch("evohomeasync2.zone.Zone.set_temperature") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.SET_ZONE_OVERRIDE,
|
||||
{
|
||||
ATTR_ENTITY_ID: zone_id,
|
||||
ATTR_SETPOINT: 19.5,
|
||||
ATTR_DURATION: {"minutes": 135},
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with(
|
||||
19.5, until=datetime(2024, 7, 10, 14, 15, tzinfo=UTC)
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
"""The tests for evohome."""
|
||||
"""The tests for Evohome."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
@ -11,7 +11,7 @@ from evohomeasync2 import EvohomeClient, exceptions as exc
|
|||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.evohome.const import DOMAIN, EvoService
|
||||
from homeassistant.components.evohome.const import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
@ -187,41 +187,3 @@ async def test_setup(
|
|||
"""
|
||||
|
||||
assert hass.services.async_services_for_domain(DOMAIN).keys() == snapshot
|
||||
|
||||
|
||||
@pytest.mark.parametrize("install", ["default"])
|
||||
async def test_service_refresh_system(
|
||||
hass: HomeAssistant,
|
||||
evohome: EvohomeClient,
|
||||
) -> None:
|
||||
"""Test EvoService.REFRESH_SYSTEM of an evohome system."""
|
||||
|
||||
# EvoService.REFRESH_SYSTEM
|
||||
with patch("evohomeasync2.location.Location.update") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.REFRESH_SYSTEM,
|
||||
{},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("install", ["default"])
|
||||
async def test_service_reset_system(
|
||||
hass: HomeAssistant,
|
||||
evohome: EvohomeClient,
|
||||
) -> None:
|
||||
"""Test EvoService.RESET_SYSTEM of an evohome system."""
|
||||
|
||||
# EvoService.RESET_SYSTEM (if SZ_AUTO_WITH_RESET in modes)
|
||||
with patch("evohomeasync2.control_system.ControlSystem.set_mode") as mock_fcn:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
EvoService.RESET_SYSTEM,
|
||||
{},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
mock_fcn.assert_awaited_once_with("AutoWithReset", until=None)
|
||||
|
|
Loading…
Reference in New Issue