Add tests to miele fan entity and api push data pathway (#144481)

* Use device class transation

* WIP

* Test api push

* Use constants

* Use callbacks registered with mock

* Add comment

* Adress review comments

* Empty commit

* Fix tests

* Updates after review
This commit is contained in:
Åke Strandberg 2025-05-26 16:38:41 +02:00 committed by GitHub
parent 6f9a39ab89
commit 42cacd28e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 3294 additions and 21 deletions

View File

@ -197,13 +197,13 @@ class MieleClimate(MieleEntity, ClimateEntity):
self._attr_name = None
if description.zone == 2:
t_key = "zone_2"
if self.device.device_type in (
MieleAppliance.FRIDGE_FREEZER,
MieleAppliance.WINE_CABINET_FREEZER,
):
t_key = DEVICE_TYPE_TAGS[MieleAppliance.FREEZER]
else:
t_key = "zone_2"
elif description.zone == 3:
t_key = "zone_3"
@ -234,11 +234,11 @@ class MieleClimate(MieleEntity, ClimateEntity):
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
return
try:
await self.api.set_target_temperature(
self._device_id, temperature, self.entity_description.zone
self._device_id,
cast(float, kwargs.get(ATTR_TEMPERATURE)),
self.entity_description.zone,
)
except aiohttp.ClientError as err:
raise HomeAssistantError(

View File

@ -1,5 +1,8 @@
"""Tests for the Miele integration."""
from collections.abc import Awaitable, Callable
from unittest.mock import AsyncMock
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
@ -11,3 +14,13 @@ async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
def get_data_callback(mock: AsyncMock) -> Callable[[int], Awaitable[None]]:
"""Get registered callback for api data push."""
return mock.listen_events.call_args_list[0].kwargs.get("data_callback")
def get_actions_callback(mock: AsyncMock) -> Callable[[int], Awaitable[None]]:
"""Get registered callback for api data push."""
return mock.listen_events.call_args_list[0].kwargs.get("actions_callback")

View File

@ -15,6 +15,7 @@ from homeassistant.components.miele.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from . import get_actions_callback, get_data_callback
from .const import CLIENT_ID, CLIENT_SECRET
from tests.common import MockConfigEntry, load_fixture, load_json_object_fixture
@ -157,3 +158,21 @@ def mock_setup_entry() -> Generator[AsyncMock]:
"homeassistant.components.miele.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
async def push_data_and_actions(
hass: HomeAssistant,
mock_miele_client: MagicMock,
device_fixture: MieleDevices,
) -> None:
"""Fixture to push data and actions through mock."""
data_callback = get_data_callback(mock_miele_client)
await data_callback(device_fixture)
await hass.async_block_till_done()
act_file = load_json_object_fixture("4_actions.json", DOMAIN)
action_callback = get_actions_callback(mock_miele_client)
await action_callback(act_file)
await hass.async_block_till_done()

View File

@ -0,0 +1,86 @@
{
"Dummy_Appliance_1": {
"processAction": [4],
"light": [],
"ambientLight": [],
"startTime": [],
"ventilationStep": [],
"programId": [],
"targetTemperature": [
{
"zone": 1,
"min": -27,
"max": -13
}
],
"deviceName": true,
"powerOn": false,
"powerOff": false,
"colors": [],
"modes": [1],
"runOnTime": []
},
"Dummy_Appliance_2": {
"processAction": [6],
"light": [],
"ambientLight": [],
"startTime": [],
"ventilationStep": [],
"programId": [],
"targetTemperature": [
{
"zone": 1,
"min": 1,
"max": 9
}
],
"deviceName": true,
"powerOn": false,
"powerOff": false,
"colors": [],
"modes": [1],
"runOnTime": []
},
"Dummy_Appliance_3": {
"processAction": [1, 2, 3],
"light": [],
"ambientLight": [],
"startTime": [],
"ventilationStep": [],
"programId": [],
"targetTemperature": [
{
"zone": 1,
"min": 1,
"max": 9
}
],
"deviceName": true,
"powerOn": true,
"powerOff": false,
"colors": [],
"modes": [],
"runOnTime": []
},
"DummyAppliance_18": {
"processAction": [],
"light": [],
"ambientLight": [],
"startTime": [],
"ventilationStep": [],
"programId": [],
"targetTemperature": [
{
"zone": 1,
"min": 1,
"max": 9
}
],
"deviceName": true,
"powerOn": true,
"powerOff": false,
"colors": [],
"modes": [],
"runOnTime": []
}
}

View File

@ -0,0 +1,17 @@
{
"Dummy_Vacuum_1": {
"processAction": [],
"light": [],
"ambientLight": [],
"startTime": [],
"ventilationStep": [],
"programId": [3],
"targetTemperature": [],
"deviceName": true,
"powerOn": true,
"powerOff": false,
"colors": [],
"modes": [],
"runOnTime": []
}
}

View File

@ -9,7 +9,7 @@
{
"zone": 1,
"min": -28,
"max": -14
"max": 28
}
],
"deviceName": true,

File diff suppressed because it is too large Load Diff

View File

@ -187,3 +187,191 @@
'state': 'unknown',
})
# ---
# name: test_button_states_api_push[platforms0][button.hood_stop-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': None,
'entity_id': 'button.hood_stop',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Stop',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'stop',
'unique_id': 'DummyAppliance_18-stop',
'unit_of_measurement': None,
})
# ---
# name: test_button_states_api_push[platforms0][button.hood_stop-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Hood Stop',
}),
'context': <ANY>,
'entity_id': 'button.hood_stop',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unavailable',
})
# ---
# name: test_button_states_api_push[platforms0][button.washing_machine_pause-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': None,
'entity_id': 'button.washing_machine_pause',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Pause',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'pause',
'unique_id': 'Dummy_Appliance_3-pause',
'unit_of_measurement': None,
})
# ---
# name: test_button_states_api_push[platforms0][button.washing_machine_pause-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Washing machine Pause',
}),
'context': <ANY>,
'entity_id': 'button.washing_machine_pause',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_button_states_api_push[platforms0][button.washing_machine_start-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': None,
'entity_id': 'button.washing_machine_start',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Start',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'start',
'unique_id': 'Dummy_Appliance_3-start',
'unit_of_measurement': None,
})
# ---
# name: test_button_states_api_push[platforms0][button.washing_machine_start-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Washing machine Start',
}),
'context': <ANY>,
'entity_id': 'button.washing_machine_start',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---
# name: test_button_states_api_push[platforms0][button.washing_machine_stop-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': None,
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'button',
'entity_category': None,
'entity_id': 'button.washing_machine_stop',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Stop',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'stop',
'unique_id': 'Dummy_Appliance_3-stop',
'unit_of_measurement': None,
})
# ---
# name: test_button_states_api_push[platforms0][button.washing_machine_stop-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Washing machine Stop',
}),
'context': <ANY>,
'entity_id': 'button.washing_machine_stop',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'unknown',
})
# ---

View File

@ -125,3 +125,129 @@
'state': 'cool',
})
# ---
# name: test_climate_states_api_push[platforms0-freezer][climate.freezer-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'hvac_modes': list([
<HVACMode.COOL: 'cool'>,
]),
'max_temp': -13,
'min_temp': -27,
'target_temp_step': 1.0,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.freezer',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'miele',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 1>,
'translation_key': 'freezer',
'unique_id': 'Dummy_Appliance_1-thermostat-1',
'unit_of_measurement': None,
})
# ---
# name: test_climate_states_api_push[platforms0-freezer][climate.freezer-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': -18,
'friendly_name': 'Freezer',
'hvac_modes': list([
<HVACMode.COOL: 'cool'>,
]),
'max_temp': -13,
'min_temp': -27,
'supported_features': <ClimateEntityFeature: 1>,
'target_temp_step': 1.0,
'temperature': -18,
}),
'context': <ANY>,
'entity_id': 'climate.freezer',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'cool',
})
# ---
# name: test_climate_states_api_push[platforms0-freezer][climate.refrigerator-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'hvac_modes': list([
<HVACMode.COOL: 'cool'>,
]),
'max_temp': 9,
'min_temp': 1,
'target_temp_step': 1.0,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.refrigerator',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'miele',
'previous_unique_id': None,
'supported_features': <ClimateEntityFeature: 1>,
'translation_key': 'refrigerator',
'unique_id': 'Dummy_Appliance_2-thermostat-1',
'unit_of_measurement': None,
})
# ---
# name: test_climate_states_api_push[platforms0-freezer][climate.refrigerator-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'current_temperature': 4,
'friendly_name': 'Refrigerator',
'hvac_modes': list([
<HVACMode.COOL: 'cool'>,
]),
'max_temp': 9,
'min_temp': 1,
'supported_features': <ClimateEntityFeature: 1>,
'target_temp_step': 1.0,
'temperature': 4,
}),
'context': <ANY>,
'entity_id': 'climate.refrigerator',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'cool',
})
# ---

View File

@ -37,7 +37,7 @@
]),
'targetTemperature': list([
dict({
'max': -14,
'max': 28,
'min': -28,
'zone': 1,
}),
@ -70,7 +70,7 @@
]),
'targetTemperature': list([
dict({
'max': -14,
'max': 28,
'min': -28,
'zone': 1,
}),
@ -103,7 +103,7 @@
]),
'targetTemperature': list([
dict({
'max': -14,
'max': 28,
'min': -28,
'zone': 1,
}),
@ -136,7 +136,7 @@
]),
'targetTemperature': list([
dict({
'max': -14,
'max': 28,
'min': -28,
'zone': 1,
}),
@ -710,7 +710,7 @@
]),
'targetTemperature': list([
dict({
'max': -14,
'max': 28,
'min': -28,
'zone': 1,
}),

View File

@ -151,3 +151,57 @@
'state': 'off',
})
# ---
# name: test_fan_states_api_push[platforms0][fan.hood_fan-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'preset_modes': None,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'fan',
'entity_category': None,
'entity_id': 'fan.hood_fan',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Fan',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': <FanEntityFeature: 49>,
'translation_key': 'fan',
'unique_id': 'DummyAppliance_18-fan',
'unit_of_measurement': None,
})
# ---
# name: test_fan_states_api_push[platforms0][fan.hood_fan-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Hood Fan',
'percentage': 0,
'percentage_step': 25.0,
'preset_mode': None,
'preset_modes': None,
'supported_features': <FanEntityFeature: 49>,
}),
'context': <ANY>,
'entity_id': 'fan.hood_fan',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -111,3 +111,115 @@
'state': 'on',
})
# ---
# name: test_light_states_api_push[platforms0][light.hood_ambient_light-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'supported_color_modes': list([
<ColorMode.ONOFF: 'onoff'>,
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.hood_ambient_light',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Ambient light',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'ambient_light',
'unique_id': 'DummyAppliance_18-ambient_light',
'unit_of_measurement': None,
})
# ---
# name: test_light_states_api_push[platforms0][light.hood_ambient_light-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'color_mode': None,
'friendly_name': 'Hood Ambient light',
'supported_color_modes': list([
<ColorMode.ONOFF: 'onoff'>,
]),
'supported_features': <LightEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'light.hood_ambient_light',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_light_states_api_push[platforms0][light.hood_light-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'supported_color_modes': list([
<ColorMode.ONOFF: 'onoff'>,
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.hood_light',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Light',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'light',
'unique_id': 'DummyAppliance_18-light',
'unit_of_measurement': None,
})
# ---
# name: test_light_states_api_push[platforms0][light.hood_light-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'color_mode': <ColorMode.ONOFF: 'onoff'>,
'friendly_name': 'Hood Light',
'supported_color_modes': list([
<ColorMode.ONOFF: 'onoff'>,
]),
'supported_features': <LightEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'light.hood_light',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

File diff suppressed because it is too large Load Diff

View File

@ -187,3 +187,191 @@
'state': 'off',
})
# ---
# name: test_switch_states_api_push[platforms0][switch.freezer_superfreezing-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'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.freezer_superfreezing',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Superfreezing',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'superfreezing',
'unique_id': 'Dummy_Appliance_1-superfreezing',
'unit_of_measurement': None,
})
# ---
# name: test_switch_states_api_push[platforms0][switch.freezer_superfreezing-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Freezer Superfreezing',
}),
'context': <ANY>,
'entity_id': 'switch.freezer_superfreezing',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch_states_api_push[platforms0][switch.hood_power-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'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.hood_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Power',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'power',
'unique_id': 'DummyAppliance_18-poweronoff',
'unit_of_measurement': None,
})
# ---
# name: test_switch_states_api_push[platforms0][switch.hood_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Hood Power',
}),
'context': <ANY>,
'entity_id': 'switch.hood_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch_states_api_push[platforms0][switch.refrigerator_supercooling-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'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.refrigerator_supercooling',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Supercooling',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'supercooling',
'unique_id': 'Dummy_Appliance_2-supercooling',
'unit_of_measurement': None,
})
# ---
# name: test_switch_states_api_push[platforms0][switch.refrigerator_supercooling-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Refrigerator Supercooling',
}),
'context': <ANY>,
'entity_id': 'switch.refrigerator_supercooling',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_switch_states_api_push[platforms0][switch.washing_machine_power-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'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.washing_machine_power',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Power',
'platform': 'miele',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'power',
'unique_id': 'Dummy_Appliance_3-poweronoff',
'unit_of_measurement': None,
})
# ---
# name: test_switch_states_api_push[platforms0][switch.washing_machine_power-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Washing machine Power',
}),
'context': <ANY>,
'entity_id': 'switch.washing_machine_power',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---

View File

@ -61,3 +61,65 @@
'state': 'cleaning',
})
# ---
# name: test_vacuum_states_api_push[platforms0-vacuum_device.json][vacuum.robot_vacuum_cleaner-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'fan_speed_list': list([
'normal',
'turbo',
'silent',
]),
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'vacuum',
'entity_category': None,
'entity_id': 'vacuum.robot_vacuum_cleaner',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'miele',
'previous_unique_id': None,
'supported_features': <VacuumEntityFeature: 13420>,
'translation_key': 'vacuum',
'unique_id': 'Dummy_Vacuum_1-vacuum',
'unit_of_measurement': None,
})
# ---
# name: test_vacuum_states_api_push[platforms0-vacuum_device.json][vacuum.robot_vacuum_cleaner-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'battery_icon': 'mdi:battery-60',
'battery_level': 65,
'fan_speed': 'normal',
'fan_speed_list': list([
'normal',
'turbo',
'silent',
]),
'friendly_name': 'Robot vacuum cleaner',
'supported_features': <VacuumEntityFeature: 13420>,
}),
'context': <ANY>,
'entity_id': 'vacuum.robot_vacuum_cleaner',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'cleaning',
})
# ---

View File

@ -24,3 +24,18 @@ async def test_binary_sensor_states(
"""Test binary sensor state."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("platforms", [(BINARY_SENSOR_DOMAIN,)])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_binary_sensor_states_api_push(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
push_data_and_actions: None,
) -> None:
"""Test binary sensor state when the API pushes data via SSE."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)

View File

@ -33,6 +33,20 @@ async def test_button_states(
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_button_states_api_push(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
push_data_and_actions: None,
) -> None:
"""Test binary sensor state when the API pushes data via SSE."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_button_press(
hass: HomeAssistant,
@ -58,7 +72,9 @@ async def test_api_failure(
"""Test handling of exception from API."""
mock_miele_client.send_action.side_effect = ClientResponseError("test", "Test")
with pytest.raises(HomeAssistantError):
with pytest.raises(
HomeAssistantError, match=f"Failed to set state for {ENTITY_ID}"
):
await hass.services.async_call(
TEST_PLATFORM, SERVICE_PRESS, {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True
)

View File

@ -42,6 +42,20 @@ async def test_climate_states(
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_climate_states_api_push(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
push_data_and_actions: None,
) -> None:
"""Test climate state when the API pushes data via SSE."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
async def test_set_target(
hass: HomeAssistant,
mock_miele_client: MagicMock,
@ -68,7 +82,9 @@ async def test_api_failure(
"""Test handling of exception from API."""
mock_miele_client.set_target_temperature.side_effect = ClientError
with pytest.raises(HomeAssistantError):
with pytest.raises(
HomeAssistantError, match=f"Failed to set state for {ENTITY_ID}"
):
await hass.services.async_call(
TEST_PLATFORM,
SERVICE_SET_TEMPERATURE,

View File

@ -7,7 +7,11 @@ from aiohttp import ClientResponseError
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.fan import ATTR_PERCENTAGE, DOMAIN as FAN_DOMAIN
from homeassistant.components.fan import (
ATTR_PERCENTAGE,
DOMAIN as FAN_DOMAIN,
SERVICE_SET_PERCENTAGE,
)
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
@ -34,6 +38,20 @@ async def test_fan_states(
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_fan_states_api_push(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
push_data_and_actions: None,
) -> None:
"""Test fan state when the API pushes data via SSE."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("load_device_file", ["fan_devices.json"])
@pytest.mark.parametrize(
("service", "expected_argument"),
@ -78,7 +96,7 @@ async def test_fan_set_speed(
percentage: int,
expected_argument: dict[str, Any],
) -> None:
"""Test the fan can be turned on/off."""
"""Test the fan can set percentage."""
await hass.services.async_call(
TEST_PLATFORM,
@ -91,6 +109,24 @@ async def test_fan_set_speed(
)
async def test_fan_turn_on_w_percentage(
hass: HomeAssistant,
mock_miele_client: MagicMock,
setup_platform: None,
) -> None:
"""Test the fan can turn on with percentage."""
await hass.services.async_call(
TEST_PLATFORM,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PERCENTAGE: 50},
blocking=True,
)
mock_miele_client.send_action.assert_called_with(
"DummyAppliance_18", {"ventilationStep": 2}
)
@pytest.mark.parametrize(
("service"),
[
@ -112,3 +148,23 @@ async def test_api_failure(
TEST_PLATFORM, service, {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True
)
mock_miele_client.send_action.assert_called_once()
async def test_set_percentage(
hass: HomeAssistant,
mock_miele_client: MagicMock,
setup_platform: None,
) -> None:
"""Test handling of exception at set_percentage."""
mock_miele_client.send_action.side_effect = ClientResponseError("test", "Test")
with pytest.raises(
HomeAssistantError, match=f"Failed to set state for {ENTITY_ID}"
):
await hass.services.async_call(
TEST_PLATFORM,
SERVICE_SET_PERCENTAGE,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_PERCENTAGE: 50},
blocking=True,
)
mock_miele_client.send_action.assert_called_once()

View File

@ -32,6 +32,20 @@ async def test_light_states(
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_light_states_api_push(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
push_data_and_actions: None,
) -> None:
"""Test light state when the API pushes data via SSE."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize(
("service", "light_state"),
[
@ -72,7 +86,9 @@ async def test_api_failure(
"""Test handling of exception from API."""
mock_miele_client.send_action.side_effect = ClientError
with pytest.raises(HomeAssistantError):
with pytest.raises(
HomeAssistantError, match=f"Failed to set state for {ENTITY_ID}"
):
await hass.services.async_call(
TEST_PLATFORM, service, {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True
)

View File

@ -21,7 +21,22 @@ async def test_sensor_states(
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
) -> None:
"""Test sensor state."""
"""Test sensor state after polling the API for data."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize("platforms", [(SENSOR_DOMAIN,)])
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_sensor_states_api_push(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
push_data_and_actions: None,
) -> None:
"""Test sensor state when the API pushes data via SSE."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)

View File

@ -32,6 +32,20 @@ async def test_switch_states(
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_switch_states_api_push(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
push_data_and_actions: None,
) -> None:
"""Test switch state when the API pushes data via SSE."""
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize(
("entity"),
[
@ -87,7 +101,7 @@ async def test_api_failure(
"""Test handling of exception from API."""
mock_miele_client.send_action.side_effect = ClientError
with pytest.raises(HomeAssistantError):
with pytest.raises(HomeAssistantError, match=f"Failed to set state for {entity}"):
await hass.services.async_call(
TEST_PLATFORM, service, {ATTR_ENTITY_ID: entity}, blocking=True
)

View File

@ -3,10 +3,11 @@
from unittest.mock import MagicMock
from aiohttp import ClientResponseError
from pymiele import MieleDevices
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.miele.const import PROCESS_ACTION, PROGRAM_ID
from homeassistant.components.miele.const import DOMAIN, PROCESS_ACTION, PROGRAM_ID
from homeassistant.components.vacuum import (
ATTR_FAN_SPEED,
DOMAIN as VACUUM_DOMAIN,
@ -21,7 +22,9 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, snapshot_platform
from . import get_actions_callback, get_data_callback
from tests.common import MockConfigEntry, load_json_object_fixture, snapshot_platform
TEST_PLATFORM = VACUUM_DOMAIN
ENTITY_ID = "vacuum.robot_vacuum_cleaner"
@ -46,6 +49,29 @@ async def test_sensor_states(
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_vacuum_states_api_push(
hass: HomeAssistant,
mock_miele_client: MagicMock,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
setup_platform: MockConfigEntry,
device_fixture: MieleDevices,
) -> None:
"""Test vacuum state when the API pushes data via SSE."""
data_callback = get_data_callback(mock_miele_client)
await data_callback(device_fixture)
await hass.async_block_till_done()
act_file = load_json_object_fixture("action_push_vacuum.json", DOMAIN)
action_callback = get_actions_callback(mock_miele_client)
await action_callback(act_file)
await hass.async_block_till_done()
await snapshot_platform(hass, entity_registry, snapshot, setup_platform.entry_id)
@pytest.mark.parametrize(
("service", "action_command", "vacuum_power"),
[
@ -112,7 +138,9 @@ async def test_api_failure(
"""Test handling of exception from API."""
mock_miele_client.send_action.side_effect = ClientResponseError("test", "Test")
with pytest.raises(HomeAssistantError):
with pytest.raises(
HomeAssistantError, match=f"Failed to set state for {ENTITY_ID}"
):
await hass.services.async_call(
TEST_PLATFORM, service, {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True
)