Sensibo Add timer (#73072)
parent
dca4d3cd61
commit
c660fae8d8
|
@ -1,9 +1,9 @@
|
|||
"""Binary Sensor platform for Sensibo integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, Mapping
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pysensibo.model import MotionSensor, SensiboDevice
|
||||
|
||||
|
@ -36,6 +36,7 @@ class DeviceBaseEntityDescriptionMixin:
|
|||
"""Mixin for required Sensibo base description keys."""
|
||||
|
||||
value_fn: Callable[[SensiboDevice], bool | None]
|
||||
extra_fn: Callable[[SensiboDevice], dict[str, str | bool | None] | None] | None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -77,13 +78,25 @@ MOTION_SENSOR_TYPES: tuple[SensiboMotionBinarySensorEntityDescription, ...] = (
|
|||
),
|
||||
)
|
||||
|
||||
DEVICE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
||||
MOTION_DEVICE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
||||
SensiboDeviceBinarySensorEntityDescription(
|
||||
key="room_occupied",
|
||||
device_class=BinarySensorDeviceClass.MOTION,
|
||||
name="Room Occupied",
|
||||
icon="mdi:motion-sensor",
|
||||
value_fn=lambda data: data.room_occupied,
|
||||
extra_fn=None,
|
||||
),
|
||||
)
|
||||
|
||||
DEVICE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
||||
SensiboDeviceBinarySensorEntityDescription(
|
||||
key="timer_on",
|
||||
device_class=BinarySensorDeviceClass.RUNNING,
|
||||
name="Timer Running",
|
||||
icon="mdi:timer",
|
||||
value_fn=lambda data: data.timer_on,
|
||||
extra_fn=lambda data: {"id": data.timer_id, "turn_on": data.timer_state_on},
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -94,6 +107,7 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
|||
name="Pure Boost Enabled",
|
||||
icon="mdi:wind-power-outline",
|
||||
value_fn=lambda data: data.pure_boost_enabled,
|
||||
extra_fn=None,
|
||||
),
|
||||
SensiboDeviceBinarySensorEntityDescription(
|
||||
key="pure_ac_integration",
|
||||
|
@ -102,6 +116,7 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
|||
name="Pure Boost linked with AC",
|
||||
icon="mdi:connection",
|
||||
value_fn=lambda data: data.pure_ac_integration,
|
||||
extra_fn=None,
|
||||
),
|
||||
SensiboDeviceBinarySensorEntityDescription(
|
||||
key="pure_geo_integration",
|
||||
|
@ -110,6 +125,7 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
|||
name="Pure Boost linked with Presence",
|
||||
icon="mdi:connection",
|
||||
value_fn=lambda data: data.pure_geo_integration,
|
||||
extra_fn=None,
|
||||
),
|
||||
SensiboDeviceBinarySensorEntityDescription(
|
||||
key="pure_measure_integration",
|
||||
|
@ -118,6 +134,7 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
|||
name="Pure Boost linked with Indoor Air Quality",
|
||||
icon="mdi:connection",
|
||||
value_fn=lambda data: data.pure_measure_integration,
|
||||
extra_fn=None,
|
||||
),
|
||||
SensiboDeviceBinarySensorEntityDescription(
|
||||
key="pure_prime_integration",
|
||||
|
@ -126,6 +143,7 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceBinarySensorEntityDescription, ...] = (
|
|||
name="Pure Boost linked with Outdoor Air Quality",
|
||||
icon="mdi:connection",
|
||||
value_fn=lambda data: data.pure_prime_integration,
|
||||
extra_fn=None,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -148,11 +166,17 @@ async def async_setup_entry(
|
|||
for sensor_id, sensor_data in device_data.motion_sensors.items()
|
||||
for description in MOTION_SENSOR_TYPES
|
||||
)
|
||||
entities.extend(
|
||||
SensiboDeviceSensor(coordinator, device_id, description)
|
||||
for description in MOTION_DEVICE_SENSOR_TYPES
|
||||
for device_id, device_data in coordinator.data.parsed.items()
|
||||
if device_data.motion_sensors is not None
|
||||
)
|
||||
entities.extend(
|
||||
SensiboDeviceSensor(coordinator, device_id, description)
|
||||
for description in DEVICE_SENSOR_TYPES
|
||||
for device_id, device_data in coordinator.data.parsed.items()
|
||||
if getattr(device_data, description.key) is not None
|
||||
if device_data.model != "pure"
|
||||
)
|
||||
entities.extend(
|
||||
SensiboDeviceSensor(coordinator, device_id, description)
|
||||
|
@ -223,3 +247,10 @@ class SensiboDeviceSensor(SensiboDeviceBaseEntity, BinarySensorEntity):
|
|||
def is_on(self) -> bool | None:
|
||||
"""Return true if the binary sensor is on."""
|
||||
return self.entity_description.value_fn(self.device_data)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
"""Return additional attributes."""
|
||||
if self.entity_description.extra_fn is not None:
|
||||
return self.entity_description.extra_fn(self.device_data)
|
||||
return None
|
||||
|
|
|
@ -18,7 +18,7 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_platform
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util.temperature import convert as convert_temperature
|
||||
|
||||
|
@ -27,6 +27,8 @@ from .coordinator import SensiboDataUpdateCoordinator
|
|||
from .entity import SensiboDeviceBaseEntity
|
||||
|
||||
SERVICE_ASSUME_STATE = "assume_state"
|
||||
SERVICE_TIMER = "timer"
|
||||
ATTR_MINUTES = "minutes"
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
FIELD_TO_FLAG = {
|
||||
|
@ -85,6 +87,14 @@ async def async_setup_entry(
|
|||
},
|
||||
"async_assume_state",
|
||||
)
|
||||
platform.async_register_entity_service(
|
||||
SERVICE_TIMER,
|
||||
{
|
||||
vol.Required(ATTR_STATE): vol.In(["on", "off"]),
|
||||
vol.Optional(ATTR_MINUTES): cv.positive_int,
|
||||
},
|
||||
"async_set_timer",
|
||||
)
|
||||
|
||||
|
||||
class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
||||
|
@ -276,3 +286,25 @@ class SensiboClimate(SensiboDeviceBaseEntity, ClimateEntity):
|
|||
"""Sync state with api."""
|
||||
await self._async_set_ac_state_property("on", state != HVACMode.OFF, True)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_set_timer(self, state: str, minutes: int | None = None) -> None:
|
||||
"""Set or delete timer."""
|
||||
if state == "off" and self.device_data.timer_id is None:
|
||||
raise HomeAssistantError("No timer to delete")
|
||||
|
||||
if state == "on" and minutes is None:
|
||||
raise ValueError("No value provided for timer")
|
||||
|
||||
if state == "off":
|
||||
result = await self.async_send_command("del_timer")
|
||||
else:
|
||||
new_state = bool(self.device_data.ac_states["on"] is False)
|
||||
params = {
|
||||
"minutesFromNow": minutes,
|
||||
"acState": {**self.device_data.ac_states, "on": new_state},
|
||||
}
|
||||
result = await self.async_send_command("set_timer", params)
|
||||
|
||||
if result["status"] == "success":
|
||||
return await self.coordinator.async_request_refresh()
|
||||
raise HomeAssistantError(f"Could not set timer for device {self.name}")
|
||||
|
|
|
@ -57,7 +57,7 @@ class SensiboDeviceBaseEntity(SensiboBaseEntity):
|
|||
)
|
||||
|
||||
async def async_send_command(
|
||||
self, command: str, params: dict[str, Any]
|
||||
self, command: str, params: dict[str, Any] | None = None
|
||||
) -> dict[str, Any]:
|
||||
"""Send command to Sensibo api."""
|
||||
try:
|
||||
|
@ -72,16 +72,20 @@ class SensiboDeviceBaseEntity(SensiboBaseEntity):
|
|||
return result
|
||||
|
||||
async def async_send_api_call(
|
||||
self, command: str, params: dict[str, Any]
|
||||
self, command: str, params: dict[str, Any] | None = None
|
||||
) -> dict[str, Any]:
|
||||
"""Send api call."""
|
||||
result: dict[str, Any] = {"status": None}
|
||||
if command == "set_calibration":
|
||||
if TYPE_CHECKING:
|
||||
assert params is not None
|
||||
result = await self._client.async_set_calibration(
|
||||
self._device_id,
|
||||
params["data"],
|
||||
)
|
||||
if command == "set_ac_state":
|
||||
if TYPE_CHECKING:
|
||||
assert params is not None
|
||||
result = await self._client.async_set_ac_state_property(
|
||||
self._device_id,
|
||||
params["name"],
|
||||
|
@ -89,6 +93,12 @@ class SensiboDeviceBaseEntity(SensiboBaseEntity):
|
|||
params["ac_states"],
|
||||
params["assumed_state"],
|
||||
)
|
||||
if command == "set_timer":
|
||||
if TYPE_CHECKING:
|
||||
assert params is not None
|
||||
result = await self._client.async_set_timer(self._device_id, params)
|
||||
if command == "del_timer":
|
||||
result = await self._client.async_del_timer(self._device_id)
|
||||
return result
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
"""Sensor platform for Sensibo integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from collections.abc import Callable, Mapping
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from pysensibo.model import MotionSensor, SensiboDevice
|
||||
|
||||
|
@ -44,7 +45,8 @@ class MotionBaseEntityDescriptionMixin:
|
|||
class DeviceBaseEntityDescriptionMixin:
|
||||
"""Mixin for required Sensibo base description keys."""
|
||||
|
||||
value_fn: Callable[[SensiboDevice], StateType]
|
||||
value_fn: Callable[[SensiboDevice], StateType | datetime]
|
||||
extra_fn: Callable[[SensiboDevice], dict[str, str | bool | None] | None] | None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -111,13 +113,25 @@ PURE_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = (
|
|||
name="PM2.5",
|
||||
icon="mdi:air-filter",
|
||||
value_fn=lambda data: data.pm25,
|
||||
extra_fn=None,
|
||||
),
|
||||
SensiboDeviceSensorEntityDescription(
|
||||
key="pure_sensitivity",
|
||||
name="Pure Sensitivity",
|
||||
icon="mdi:air-filter",
|
||||
value_fn=lambda data: str(data.pure_sensitivity).lower(),
|
||||
device_class="sensibo__sensitivity",
|
||||
value_fn=lambda data: data.pure_sensitivity,
|
||||
extra_fn=None,
|
||||
),
|
||||
)
|
||||
|
||||
DEVICE_SENSOR_TYPES: tuple[SensiboDeviceSensorEntityDescription, ...] = (
|
||||
SensiboDeviceSensorEntityDescription(
|
||||
key="timer_time",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
name="Timer End Time",
|
||||
icon="mdi:timer",
|
||||
value_fn=lambda data: data.timer_time,
|
||||
extra_fn=lambda data: {"id": data.timer_id, "turn_on": data.timer_state_on},
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -146,6 +160,12 @@ async def async_setup_entry(
|
|||
for description in PURE_SENSOR_TYPES
|
||||
if device_data.model == "pure"
|
||||
)
|
||||
entities.extend(
|
||||
SensiboDeviceSensor(coordinator, device_id, description)
|
||||
for device_id, device_data in coordinator.data.parsed.items()
|
||||
for description in DEVICE_SENSOR_TYPES
|
||||
if device_data.model != "pure"
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
|
@ -205,6 +225,13 @@ class SensiboDeviceSensor(SensiboDeviceBaseEntity, SensorEntity):
|
|||
self._attr_name = f"{self.device_data.name} {entity_description.name}"
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
def native_value(self) -> StateType | datetime:
|
||||
"""Return value of sensor."""
|
||||
return self.entity_description.value_fn(self.device_data)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> Mapping[str, Any] | None:
|
||||
"""Return additional attributes."""
|
||||
if self.entity_description.extra_fn is not None:
|
||||
return self.entity_description.extra_fn(self.device_data)
|
||||
return None
|
||||
|
|
|
@ -16,3 +16,31 @@ assume_state:
|
|||
options:
|
||||
- "on"
|
||||
- "off"
|
||||
timer:
|
||||
name: Timer
|
||||
description: Set or delete timer for device.
|
||||
target:
|
||||
entity:
|
||||
integration: sensibo
|
||||
domain: climate
|
||||
fields:
|
||||
state:
|
||||
name: State
|
||||
description: Timer on or off.
|
||||
required: true
|
||||
example: "on"
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- "on"
|
||||
- "off"
|
||||
minutes:
|
||||
name: Minutes
|
||||
description: Countdown for timer (for timer state on)
|
||||
required: false
|
||||
example: 30
|
||||
selector:
|
||||
number:
|
||||
min: 0
|
||||
step: 1
|
||||
mode: box
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"""The test for the sensibo binary sensor platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from pysensibo.model import SensiboData
|
||||
import pytest
|
||||
|
@ -21,7 +21,9 @@ from homeassistant.components.climate.const import (
|
|||
SERVICE_SET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.components.sensibo.climate import (
|
||||
ATTR_MINUTES,
|
||||
SERVICE_ASSUME_STATE,
|
||||
SERVICE_TIMER,
|
||||
_find_valid_target_temp,
|
||||
)
|
||||
from homeassistant.components.sensibo.const import DOMAIN
|
||||
|
@ -32,6 +34,7 @@ from homeassistant.const import (
|
|||
ATTR_TEMPERATURE,
|
||||
SERVICE_TURN_OFF,
|
||||
SERVICE_TURN_ON,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
@ -675,3 +678,230 @@ async def test_climate_no_fan_no_swing(
|
|||
assert state.attributes["swing_mode"] is None
|
||||
assert state.attributes["fan_modes"] is None
|
||||
assert state.attributes["swing_modes"] is None
|
||||
|
||||
|
||||
async def test_climate_set_timer(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: AsyncMock,
|
||||
load_int: ConfigEntry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
get_data: SensiboData,
|
||||
) -> None:
|
||||
"""Test the Sensibo climate Set Timer service."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
):
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
dt.utcnow() + timedelta(minutes=5),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state1 = hass.states.get("climate.hallway")
|
||||
assert hass.states.get("sensor.hallway_timer_end_time").state == STATE_UNKNOWN
|
||||
assert hass.states.get("binary_sensor.hallway_timer_running").state == "off"
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_timer",
|
||||
return_value={"status": "success", "result": {"id": "SzTGE4oZ4D"}},
|
||||
):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_TIMER,
|
||||
{
|
||||
ATTR_ENTITY_ID: state1.entity_id,
|
||||
ATTR_STATE: "on",
|
||||
ATTR_MINUTES: 30,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_on", True)
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_id", "SzTGE4oZ4D")
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_state_on", False)
|
||||
monkeypatch.setattr(
|
||||
get_data.parsed["ABC999111"],
|
||||
"timer_time",
|
||||
datetime(2022, 6, 6, 12, 00, 00, tzinfo=dt.UTC),
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
):
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
dt.utcnow() + timedelta(minutes=5),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
hass.states.get("sensor.hallway_timer_end_time").state
|
||||
== "2022-06-06T12:00:00+00:00"
|
||||
)
|
||||
assert hass.states.get("binary_sensor.hallway_timer_running").state == "on"
|
||||
assert hass.states.get("binary_sensor.hallway_timer_running").attributes == {
|
||||
"device_class": "running",
|
||||
"friendly_name": "Hallway Timer Running",
|
||||
"icon": "mdi:timer",
|
||||
"id": "SzTGE4oZ4D",
|
||||
"turn_on": False,
|
||||
}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_del_timer",
|
||||
return_value={"status": "success"},
|
||||
):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_TIMER,
|
||||
{
|
||||
ATTR_ENTITY_ID: state1.entity_id,
|
||||
ATTR_STATE: "off",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_on", False)
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_id", None)
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_state_on", None)
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_time", None)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
):
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
dt.utcnow() + timedelta(minutes=5),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("sensor.hallway_timer_end_time").state == STATE_UNKNOWN
|
||||
assert hass.states.get("binary_sensor.hallway_timer_running").state == "off"
|
||||
|
||||
|
||||
async def test_climate_set_timer_failures(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: AsyncMock,
|
||||
load_int: ConfigEntry,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
get_data: SensiboData,
|
||||
) -> None:
|
||||
"""Test the Sensibo climate Set Timer service failures."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
):
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
dt.utcnow() + timedelta(minutes=5),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state1 = hass.states.get("climate.hallway")
|
||||
assert hass.states.get("sensor.hallway_timer_end_time").state == STATE_UNKNOWN
|
||||
assert hass.states.get("binary_sensor.hallway_timer_running").state == "off"
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_TIMER,
|
||||
{
|
||||
ATTR_ENTITY_ID: state1.entity_id,
|
||||
ATTR_STATE: "on",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_timer",
|
||||
return_value={"status": "success", "result": {"id": ""}},
|
||||
):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_TIMER,
|
||||
{
|
||||
ATTR_ENTITY_ID: state1.entity_id,
|
||||
ATTR_STATE: "on",
|
||||
ATTR_MINUTES: 30,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_on", True)
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_id", None)
|
||||
monkeypatch.setattr(get_data.parsed["ABC999111"], "timer_state_on", False)
|
||||
monkeypatch.setattr(
|
||||
get_data.parsed["ABC999111"],
|
||||
"timer_time",
|
||||
datetime(2022, 6, 6, 12, 00, 00, tzinfo=dt.UTC),
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
):
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
dt.utcnow() + timedelta(minutes=5),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_TIMER,
|
||||
{
|
||||
ATTR_ENTITY_ID: state1.entity_id,
|
||||
ATTR_STATE: "off",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.coordinator.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
):
|
||||
async_fire_time_changed(
|
||||
hass,
|
||||
dt.utcnow() + timedelta(minutes=5),
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_get_devices_data",
|
||||
return_value=get_data,
|
||||
), patch(
|
||||
"homeassistant.components.sensibo.util.SensiboClient.async_set_timer",
|
||||
return_value={"status": "failure"},
|
||||
):
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_TIMER,
|
||||
{
|
||||
ATTR_ENTITY_ID: state1.entity_id,
|
||||
ATTR_STATE: "on",
|
||||
ATTR_MINUTES: 30,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
|
Loading…
Reference in New Issue