Vesync add oscillation to fan (#153297)

This commit is contained in:
cdnninja
2025-10-06 14:50:11 +02:00
committed by GitHub
parent c36341e51f
commit 425bdc0ba6
5 changed files with 75 additions and 4 deletions
+23 -1
View File
@@ -18,7 +18,7 @@ from homeassistant.util.percentage import (
percentage_to_ordered_list_item,
)
from .common import is_fan, is_purifier
from .common import is_fan, is_purifier, rgetattr
from .const import (
DOMAIN,
VS_COORDINATOR,
@@ -90,11 +90,26 @@ class VeSyncFanHA(VeSyncBaseEntity, FanEntity):
_attr_name = None
_attr_translation_key = "vesync"
def __init__(
self,
device: VeSyncBaseDevice,
coordinator: VeSyncDataCoordinator,
) -> None:
"""Initialize the fan."""
super().__init__(device, coordinator)
if rgetattr(device, "state.oscillation_status") is not None:
self._attr_supported_features |= FanEntityFeature.OSCILLATE
@property
def is_on(self) -> bool:
"""Return True if device is on."""
return self.device.state.device_status == "on"
@property
def oscillating(self) -> bool:
"""Return True if device is oscillating."""
return rgetattr(self.device, "state.oscillation_status") == "on"
@property
def percentage(self) -> int | None:
"""Return the currently set speed."""
@@ -248,3 +263,10 @@ class VeSyncFanHA(VeSyncBaseEntity, FanEntity):
if not success:
raise HomeAssistantError(self.device.last_response.message)
self.schedule_update_ha_state()
async def async_oscillate(self, oscillating: bool) -> None:
"""Set oscillation."""
success = await self.device.toggle_oscillation(oscillating)
if not success:
raise HomeAssistantError(self.device.last_response.message)
self.schedule_update_ha_state()
@@ -250,7 +250,7 @@
'friendly_name': 'Test Fan',
'preset_modes': list([
]),
'supported_features': 57,
'supported_features': 59,
}),
'entity_id': 'fan.test_fan',
'last_changed': str,
@@ -656,7 +656,7 @@
'platform': 'vesync',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': <FanEntityFeature: 57>,
'supported_features': <FanEntityFeature: 59>,
'translation_key': 'vesync',
'unique_id': 'smarttowerfan',
'unit_of_measurement': None,
@@ -670,6 +670,7 @@
'display_status': 'off',
'friendly_name': 'SmartTowerFan',
'mode': 'normal',
'oscillating': True,
'percentage': None,
'percentage_step': 8.333333333333334,
'preset_mode': 'normal',
@@ -679,7 +680,7 @@
'normal',
'turbo',
]),
'supported_features': <FanEntityFeature: 57>,
'supported_features': <FanEntityFeature: 59>,
}),
'context': <ANY>,
'entity_id': 'fan.smarttowerfan',
@@ -107,6 +107,9 @@ async def test_async_get_device_diagnostics__single_fan(
"home_assistant.entities.6.state.last_changed": (str,),
"home_assistant.entities.6.state.last_reported": (str,),
"home_assistant.entities.6.state.last_updated": (str,),
"home_assistant.entities.7.state.last_changed": (str,),
"home_assistant.entities.7.state.last_reported": (str,),
"home_assistant.entities.7.state.last_updated": (str,),
}
)
)
+45
View File
@@ -181,3 +181,48 @@ async def test_set_preset_mode(
await hass.async_block_till_done()
method_mock.assert_called_once()
update_mock.assert_called_once()
@pytest.mark.parametrize(
("action", "command"),
[
("true", "pyvesync.devices.vesyncfan.VeSyncTowerFan.toggle_oscillation"),
("false", "pyvesync.devices.vesyncfan.VeSyncTowerFan.toggle_oscillation"),
],
)
@pytest.mark.parametrize(
("api_response", "expectation"),
[(True, NoException), (False, pytest.raises(HomeAssistantError))],
)
async def test_oscillation_success(
hass: HomeAssistant,
fan_config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker,
action: str,
command: str,
api_response: bool,
expectation,
) -> None:
"""Test oscillation on and off."""
mock_devices_response(aioclient_mock, "SmartTowerFan")
with (
expectation,
patch(
command, new_callable=AsyncMock, return_value=api_response
) as method_mock,
):
with patch(
"homeassistant.components.vesync.fan.VeSyncFanHA.schedule_update_ha_state"
) as update_mock:
await hass.services.async_call(
FAN_DOMAIN,
"oscillate",
{ATTR_ENTITY_ID: ENTITY_FAN, "oscillating": action},
blocking=True,
)
await hass.async_block_till_done()
method_mock.assert_called_once()
update_mock.assert_called_once()