Add support for eco mode of VeSync devices (#176242)

This commit is contained in:
Jan Schöppach
2026-07-26 22:30:19 +02:00
committed by GitHub
parent 31ced90b14
commit c6db69878d
6 changed files with 51 additions and 0 deletions
+2
View File
@@ -36,6 +36,7 @@ VS_FAN_MODE_TURBO = "turbo"
VS_FAN_MODE_PET = "pet"
VS_FAN_MODE_MANUAL = "manual"
VS_FAN_MODE_NORMAL = "normal"
VS_FAN_MODE_ECO = "eco"
# not a full list as manual is used as speed not present
VS_FAN_MODE_PRESET_LIST_HA = [
@@ -44,6 +45,7 @@ VS_FAN_MODE_PRESET_LIST_HA = [
VS_FAN_MODE_TURBO,
VS_FAN_MODE_PET,
VS_FAN_MODE_NORMAL,
VS_FAN_MODE_ECO,
]
NIGHT_LIGHT_LEVEL_BRIGHT = "bright"
NIGHT_LIGHT_LEVEL_DIM = "dim"
+4
View File
@@ -20,6 +20,7 @@ from .const import (
VS_DEVICES,
VS_DISCOVERY,
VS_FAN_MODE_AUTO,
VS_FAN_MODE_ECO,
VS_FAN_MODE_MANUAL,
VS_FAN_MODE_NORMAL,
VS_FAN_MODE_PET,
@@ -40,6 +41,7 @@ VS_TO_HA_MODE_MAP = {
VS_FAN_MODE_PET: VS_FAN_MODE_PET,
VS_FAN_MODE_MANUAL: VS_FAN_MODE_MANUAL,
VS_FAN_MODE_NORMAL: VS_FAN_MODE_NORMAL,
VS_FAN_MODE_ECO: VS_FAN_MODE_ECO,
}
PARALLEL_UPDATES = 1
@@ -313,6 +315,8 @@ class VeSyncFanHA(VeSyncBaseEntity[VeSyncFanBase | VeSyncPurifier], FanEntity):
elif vs_mode == VS_FAN_MODE_NORMAL:
if hasattr(self.device, "set_normal_mode"):
success = await self.device.set_normal_mode()
elif vs_mode == VS_FAN_MODE_ECO:
success = await self.device.set_mode(VS_FAN_MODE_ECO)
if not success:
if self.device.last_response:
@@ -7,6 +7,7 @@
"state": {
"advanced_sleep": "mdi:sleep",
"auto": "mdi:fan-auto",
"eco": "mdi:leaf",
"pet": "mdi:paw",
"sleep": "mdi:sleep",
"turbo": "mdi:weather-tornado"
@@ -52,6 +52,7 @@
"preset_mode": {
"state": {
"auto": "[%key:common::state::auto%]",
"eco": "Eco",
"normal": "[%key:common::state::normal%]",
"pet": "Pet",
"sleep": "Sleep",
@@ -909,6 +909,7 @@
'area_id': None,
'capabilities': dict({
<FanEntityCapabilityAttribute.PRESET_MODES: 'preset_modes'>: list([
'eco',
'normal',
'sleep',
'turbo',
@@ -958,6 +959,7 @@
<FanEntityStateAttribute.PERCENTAGE_STEP: 'percentage_step'>: 8.333333333333334,
<FanEntityStateAttribute.PRESET_MODE: 'preset_mode'>: 'normal',
<FanEntityCapabilityAttribute.PRESET_MODES: 'preset_modes'>: list([
'eco',
'normal',
'sleep',
'turbo',
+41
View File
@@ -9,6 +9,7 @@ from syrupy.assertion import SnapshotAssertion
from homeassistant.components.fan import (
ATTR_PERCENTAGE,
ATTR_PRESET_MODE,
ATTR_PRESET_MODES,
DOMAIN as FAN_DOMAIN,
)
from homeassistant.const import (
@@ -217,6 +218,46 @@ async def test_out_of_range_fan_level(
assert state.attributes[ATTR_PERCENTAGE] is None
async def test_set_preset_mode_eco(
hass: HomeAssistant,
config_entry: MockConfigEntry,
aioclient_mock: AiohttpClientMocker,
) -> None:
"""Test the eco preset is exposed, reflected, and sets the mode via set_mode."""
mock_devices_response(
aioclient_mock, "CoreBreeze 432S", details_override={"workMode": "eco"}
)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get(ENTITY_PEDESTAL_FAN)
assert state is not None
assert "eco" in state.attributes[ATTR_PRESET_MODES]
assert state.attributes[ATTR_PRESET_MODE] == "eco"
with (
patch(
"pyvesync.devices.vesyncfan.VeSyncPedestalFan.set_mode",
new_callable=AsyncMock,
return_value=True,
) as method_mock,
patch(
"homeassistant.components.vesync.fan.VeSyncFanHA.async_write_ha_state"
) as update_mock,
):
await hass.services.async_call(
FAN_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: ENTITY_PEDESTAL_FAN, ATTR_PRESET_MODE: "eco"},
blocking=True,
)
await hass.async_block_till_done()
method_mock.assert_awaited_once_with("eco")
update_mock.assert_called_once()
@pytest.mark.parametrize(
("action", "api_response", "expectation"),
[