mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 01:11:51 -04:00
Add ventilation quickmode switches to ViCare (#179430)
This commit is contained in:
@@ -12,6 +12,7 @@ PLATFORMS = [
|
||||
Platform.NUMBER,
|
||||
Platform.SELECT,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
Platform.WATER_HEATER,
|
||||
]
|
||||
|
||||
|
||||
@@ -659,6 +659,20 @@
|
||||
"name": "[%key:component::sensor::entity_component::signal_strength::name%]"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"quickmode_comfort": {
|
||||
"name": "Intensive"
|
||||
},
|
||||
"quickmode_eco": {
|
||||
"name": "Eco"
|
||||
},
|
||||
"quickmode_forced_level_four": {
|
||||
"name": "Boost"
|
||||
},
|
||||
"quickmode_silent": {
|
||||
"name": "Silent"
|
||||
}
|
||||
},
|
||||
"water_heater": {
|
||||
"domestic_hot_water": {
|
||||
"name": "Domestic hot water"
|
||||
@@ -674,6 +688,9 @@
|
||||
},
|
||||
"program_unknown": {
|
||||
"message": "Cannot translate preset {preset} into a valid ViCare program"
|
||||
},
|
||||
"quickmode_not_activated": {
|
||||
"message": "Unable to activate ViCare quickmode {quickmode}. Only one quickmode can be active at a time; another one may already be running."
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
"""Viessmann ViCare switch device."""
|
||||
|
||||
from contextlib import suppress
|
||||
import enum
|
||||
from typing import Any, override
|
||||
|
||||
from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig
|
||||
from PyViCare.PyViCareUtils import (
|
||||
PyViCareCommandError,
|
||||
PyViCareNotSupportedFeatureError,
|
||||
)
|
||||
from PyViCare.PyViCareVentilationDevice import (
|
||||
VentilationDevice as PyViCareVentilationDevice,
|
||||
)
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .const import DOMAIN
|
||||
from .entity import ViCareEntity
|
||||
from .types import ViCareConfigEntry, ViCareDevice
|
||||
|
||||
|
||||
class VentilationQuickmode(enum.StrEnum):
|
||||
"""ViCare ventilation quickmodes that can be switched on and off.
|
||||
|
||||
`standby` is used by the fan entity, `holiday` is scheduled instead of
|
||||
activated.
|
||||
"""
|
||||
|
||||
COMFORT = "comfort"
|
||||
ECO = "eco"
|
||||
FORCED_LEVEL_FOUR = "forcedLevelFour"
|
||||
SILENT = "silent"
|
||||
|
||||
|
||||
# Also used as unique id suffix, so the quickmodes cannot collide with other
|
||||
# switches that may be added for the same device later on.
|
||||
ENTITY_KEYS = {
|
||||
VentilationQuickmode.COMFORT: "quickmode_comfort",
|
||||
VentilationQuickmode.ECO: "quickmode_eco",
|
||||
VentilationQuickmode.FORCED_LEVEL_FOUR: "quickmode_forced_level_four",
|
||||
VentilationQuickmode.SILENT: "quickmode_silent",
|
||||
}
|
||||
|
||||
|
||||
def _build_entities(
|
||||
device_list: list[ViCareDevice],
|
||||
) -> list[ViCareQuickmodeSwitch]:
|
||||
"""Create ViCare switch entities for a device."""
|
||||
entities: list[ViCareQuickmodeSwitch] = []
|
||||
for device in device_list:
|
||||
if not device.api.isVentilationDevice():
|
||||
continue
|
||||
available: list[str] = []
|
||||
with suppress(PyViCareNotSupportedFeatureError):
|
||||
available = device.api.getVentilationQuickmodes()
|
||||
entities.extend(
|
||||
ViCareQuickmodeSwitch(quickmode, device.serial, device.config, device.api)
|
||||
for quickmode in VentilationQuickmode
|
||||
if quickmode in available
|
||||
)
|
||||
return entities
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ViCareConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Create the ViCare switch entities."""
|
||||
async_add_entities(
|
||||
await hass.async_add_executor_job(
|
||||
_build_entities,
|
||||
config_entry.runtime_data.devices,
|
||||
),
|
||||
# run update to have the current quickmode state on startup
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
class ViCareQuickmodeSwitch(ViCareEntity, SwitchEntity):
|
||||
"""Representation of a ViCare ventilation quickmode."""
|
||||
|
||||
_api: PyViCareVentilationDevice
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
quickmode: VentilationQuickmode,
|
||||
device_serial: str | None,
|
||||
device_config: PyViCareDeviceConfig,
|
||||
device: PyViCareVentilationDevice,
|
||||
) -> None:
|
||||
"""Initialize the switch."""
|
||||
super().__init__(ENTITY_KEYS[quickmode], device_serial, device_config, device)
|
||||
self._quickmode = quickmode
|
||||
self._attr_translation_key = ENTITY_KEYS[quickmode]
|
||||
|
||||
def update(self) -> None:
|
||||
"""Update state of the switch."""
|
||||
with self.vicare_api_handler(), suppress(PyViCareNotSupportedFeatureError):
|
||||
self._attr_is_on = self._api.getVentilationQuickmode(self._quickmode)
|
||||
|
||||
@override
|
||||
def turn_on(self, **kwargs: Any) -> None:
|
||||
"""Activate the quickmode."""
|
||||
try:
|
||||
self._api.activateVentilationQuickmode(self._quickmode)
|
||||
except PyViCareCommandError as err:
|
||||
# Any failed command lands here, but the one users hit is the
|
||||
# device refusing a second quickmode instead of switching over.
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="quickmode_not_activated",
|
||||
translation_placeholders={"quickmode": self._quickmode},
|
||||
) from err
|
||||
|
||||
@override
|
||||
def turn_off(self, **kwargs: Any) -> None:
|
||||
"""Deactivate the quickmode."""
|
||||
self._api.deactivateVentilationQuickmode(self._quickmode)
|
||||
@@ -0,0 +1,301 @@
|
||||
# serializer version: 1
|
||||
# name: test_all_entities[switch.model0_boost-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'switch',
|
||||
'entity_category': None,
|
||||
'entity_id': 'switch.model0_boost',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Boost',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Boost',
|
||||
'platform': 'vicare',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'quickmode_forced_level_four',
|
||||
'unique_id': 'gateway0_deviceSerialViAir300F-quickmode_forced_level_four',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model0_boost-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'model0 Boost',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.model0_boost',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model0_silent-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'switch',
|
||||
'entity_category': None,
|
||||
'entity_id': 'switch.model0_silent',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Silent',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Silent',
|
||||
'platform': 'vicare',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'quickmode_silent',
|
||||
'unique_id': 'gateway0_deviceSerialViAir300F-quickmode_silent',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model0_silent-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'model0 Silent',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.model0_silent',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model1_boost-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'switch',
|
||||
'entity_category': None,
|
||||
'entity_id': 'switch.model1_boost',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Boost',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Boost',
|
||||
'platform': 'vicare',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'quickmode_forced_level_four',
|
||||
'unique_id': 'gateway1_deviceId1-quickmode_forced_level_four',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model1_boost-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'model1 Boost',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.model1_boost',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model1_silent-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'switch',
|
||||
'entity_category': None,
|
||||
'entity_id': 'switch.model1_silent',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Silent',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Silent',
|
||||
'platform': 'vicare',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'quickmode_silent',
|
||||
'unique_id': 'gateway1_deviceId1-quickmode_silent',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model1_silent-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'model1 Silent',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.model1_silent',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model2_eco-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'switch',
|
||||
'entity_category': None,
|
||||
'entity_id': 'switch.model2_eco',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Eco',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Eco',
|
||||
'platform': 'vicare',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'quickmode_eco',
|
||||
'unique_id': 'gateway2_################-quickmode_eco',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model2_eco-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'model2 Eco',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.model2_eco',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model2_intensive-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'switch',
|
||||
'entity_category': None,
|
||||
'entity_id': 'switch.model2_intensive',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Intensive',
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Intensive',
|
||||
'platform': 'vicare',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'quickmode_comfort',
|
||||
'unique_id': 'gateway2_################-quickmode_comfort',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[switch.model2_intensive-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'model2 Intensive',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'switch.model2_intensive',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'off',
|
||||
})
|
||||
# ---
|
||||
@@ -0,0 +1,208 @@
|
||||
"""Test ViCare switch."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from PyViCare.PyViCareUtils import (
|
||||
PyViCareCommandError,
|
||||
PyViCareNotSupportedFeatureError,
|
||||
)
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
SERVICE_TURN_OFF,
|
||||
SERVICE_TURN_ON,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import MODULE, setup_integration
|
||||
from .conftest import Fixture, MockPyViCare
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
VENTILATION_FIXTURES: list[Fixture] = [
|
||||
Fixture({"type:ventilation"}, "vicare/ViAir300F.json"),
|
||||
Fixture({"type:ventilation"}, "vicare/VitoPure.json"),
|
||||
Fixture({"type:heatpump"}, "vicare/Vitocal222G_Vitovent300W.json"),
|
||||
]
|
||||
|
||||
|
||||
async def setup_switch_platform(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_vicare: MockPyViCare,
|
||||
) -> None:
|
||||
"""Set up the switch platform with the given mocked devices."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session.async_ensure_token_valid",
|
||||
),
|
||||
patch(
|
||||
f"{MODULE}._setup_vicare_api",
|
||||
return_value=mock_vicare.as_vicare_data(),
|
||||
),
|
||||
patch(f"{MODULE}.PLATFORMS", [Platform.SWITCH]),
|
||||
):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_all_entities(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test all entities."""
|
||||
await setup_switch_platform(
|
||||
hass, mock_config_entry, MockPyViCare(VENTILATION_FIXTURES)
|
||||
)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_switch_created_per_available_quickmode(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that only quickmodes that can be switched get an entity."""
|
||||
await setup_switch_platform(
|
||||
hass, mock_config_entry, MockPyViCare(VENTILATION_FIXTURES)
|
||||
)
|
||||
|
||||
assert set(hass.states.async_entity_ids(SWITCH_DOMAIN)) == {
|
||||
"switch.model0_boost",
|
||||
"switch.model0_silent",
|
||||
"switch.model1_boost",
|
||||
"switch.model1_silent",
|
||||
"switch.model2_intensive",
|
||||
"switch.model2_eco",
|
||||
}
|
||||
|
||||
|
||||
async def test_switch_state_follows_quickmode(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that an active quickmode is reported as on."""
|
||||
mock_vicare = MockPyViCare(VENTILATION_FIXTURES)
|
||||
activate_quickmode(mock_vicare, 2, "comfort")
|
||||
|
||||
await setup_switch_platform(hass, mock_config_entry, mock_vicare)
|
||||
|
||||
assert hass.states.get("switch.model2_intensive").state == STATE_ON
|
||||
assert hass.states.get("switch.model2_eco").state == STATE_OFF
|
||||
|
||||
|
||||
async def test_turn_on_activates_quickmode(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that turning the switch on activates the quickmode."""
|
||||
mock_vicare = MockPyViCare(VENTILATION_FIXTURES)
|
||||
await setup_switch_platform(hass, mock_config_entry, mock_vicare)
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: "switch.model2_intensive"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
device = mock_vicare.devices[2]
|
||||
device.service.setProperty.assert_called_once_with(
|
||||
device.accessor, "ventilation.quickmodes.comfort", "activate", {}
|
||||
)
|
||||
|
||||
|
||||
async def test_turn_off_deactivates_quickmode(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that turning the switch off deactivates the quickmode."""
|
||||
mock_vicare = MockPyViCare(VENTILATION_FIXTURES)
|
||||
await setup_switch_platform(hass, mock_config_entry, mock_vicare)
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{ATTR_ENTITY_ID: "switch.model0_boost"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
device = mock_vicare.devices[0]
|
||||
device.service.setProperty.assert_called_once_with(
|
||||
device.accessor, "ventilation.quickmodes.forcedLevelFour", "deactivate", {}
|
||||
)
|
||||
|
||||
|
||||
async def test_no_switch_for_non_ventilation_device(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that a device without ventilation does not get quickmode switches."""
|
||||
await setup_switch_platform(
|
||||
hass,
|
||||
mock_config_entry,
|
||||
MockPyViCare([Fixture({"type:boiler"}, "vicare/Vitodens300W.json")]),
|
||||
)
|
||||
|
||||
assert not hass.states.async_entity_ids(SWITCH_DOMAIN)
|
||||
|
||||
|
||||
async def test_turn_on_error_is_raised(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that a failed activation is not swallowed."""
|
||||
mock_vicare = MockPyViCare(VENTILATION_FIXTURES)
|
||||
await setup_switch_platform(hass, mock_config_entry, mock_vicare)
|
||||
mock_vicare.devices[
|
||||
2
|
||||
].service.setProperty.side_effect = PyViCareNotSupportedFeatureError("comfort")
|
||||
|
||||
with pytest.raises(PyViCareNotSupportedFeatureError):
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: "switch.model2_intensive"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_turn_on_refused_while_another_quickmode_runs(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test that the device refusing a second quickmode reads as a clear error."""
|
||||
mock_vicare = MockPyViCare(VENTILATION_FIXTURES)
|
||||
await setup_switch_platform(hass, mock_config_entry, mock_vicare)
|
||||
mock_vicare.devices[2].service.setProperty.side_effect = PyViCareCommandError(
|
||||
{"statusCode": 400, "extendedPayload": "COMMAND_NOT_EXECUTABLE"}
|
||||
)
|
||||
|
||||
with pytest.raises(ServiceValidationError) as err:
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{ATTR_ENTITY_ID: "switch.model2_intensive"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert err.value.translation_key == "quickmode_not_activated"
|
||||
|
||||
|
||||
def activate_quickmode(mock_vicare: MockPyViCare, device: int, quickmode: str) -> None:
|
||||
"""Mark a quickmode as active in the fixture data of a mocked device."""
|
||||
for feature in mock_vicare.devices[device].service._test_data["data"]:
|
||||
if feature["feature"] == f"ventilation.quickmodes.{quickmode}":
|
||||
feature["properties"]["active"]["value"] = True
|
||||
return
|
||||
pytest.fail(f"quickmode {quickmode} not found in fixture")
|
||||
Reference in New Issue
Block a user