diff --git a/homeassistant/components/vesync/fan.py b/homeassistant/components/vesync/fan.py index 9744e5062f0..21a92a22db2 100644 --- a/homeassistant/components/vesync/fan.py +++ b/homeassistant/components/vesync/fan.py @@ -161,11 +161,6 @@ class VeSyncFanHA(VeSyncBaseEntity, FanEntity): return self.smartfan.mode return None - @property - def unique_info(self): - """Return the ID of this fan.""" - return self.smartfan.uuid - @property def extra_state_attributes(self) -> dict[str, Any]: """Return the state attributes of the fan.""" diff --git a/homeassistant/components/vesync/humidifier.py b/homeassistant/components/vesync/humidifier.py index 3d89d5dc6db..8557c7a8866 100644 --- a/homeassistant/components/vesync/humidifier.py +++ b/homeassistant/components/vesync/humidifier.py @@ -137,7 +137,7 @@ class VeSyncHumidifierHA(VeSyncBaseEntity, HumidifierEntity): @property def mode(self) -> str | None: """Get the current preset mode.""" - return _get_ha_mode(self.device.mode) + return None if self.device.mode is None else _get_ha_mode(self.device.mode) def set_humidity(self, humidity: int) -> None: """Set the target humidity of the device.""" diff --git a/tests/components/vesync/conftest.py b/tests/components/vesync/conftest.py index 8272da8dfad..a80c2631088 100644 --- a/tests/components/vesync/conftest.py +++ b/tests/components/vesync/conftest.py @@ -108,7 +108,31 @@ def outlet_fixture(): @pytest.fixture(name="humidifier") def humidifier_fixture(): """Create a mock VeSync humidifier fixture.""" - return Mock(VeSyncHumid200300S) + return Mock( + VeSyncHumid200300S, + cid="200s-humidifier", + config={ + "auto_target_humidity": 40, + "display": "true", + "automatic_stop": "true", + }, + details={ + "humidity": 35, + "mode": "manual", + }, + device_type="Classic200S", + device_name="Humidifier 200s", + device_status="on", + mist_level=6, + mist_modes=["auto", "manual"], + mode=None, + sub_device_no=0, + config_module="configModule", + connection_status="online", + current_firm_version="1.0.0", + water_lacks=False, + water_tank_lifted=False, + ) @pytest.fixture(name="humidifier_config_entry") diff --git a/tests/components/vesync/fixtures/vesync-devices.json b/tests/components/vesync/fixtures/vesync-devices.json index bb32bae0435..3109fd3ea40 100644 --- a/tests/components/vesync/fixtures/vesync-devices.json +++ b/tests/components/vesync/fixtures/vesync-devices.json @@ -6,7 +6,7 @@ "cid": "200s-humidifier", "deviceType": "Classic200S", "deviceName": "Humidifier 200s", - "subDeviceNo": null, + "subDeviceNo": 4321, "deviceStatus": "on", "connectionStatus": "online", "uuid": "00000000-1111-2222-3333-444444444444", diff --git a/tests/components/vesync/snapshots/test_fan.ambr b/tests/components/vesync/snapshots/test_fan.ambr index e1b630e8d81..fddc75630d2 100644 --- a/tests/components/vesync/snapshots/test_fan.ambr +++ b/tests/components/vesync/snapshots/test_fan.ambr @@ -477,7 +477,7 @@ 'identifiers': set({ tuple( 'vesync', - '200s-humidifier', + '200s-humidifier4321', ), }), 'is_new': False, diff --git a/tests/components/vesync/snapshots/test_light.ambr b/tests/components/vesync/snapshots/test_light.ambr index 74f63ce72a1..b89cf8cdd4d 100644 --- a/tests/components/vesync/snapshots/test_light.ambr +++ b/tests/components/vesync/snapshots/test_light.ambr @@ -348,7 +348,7 @@ 'identifiers': set({ tuple( 'vesync', - '200s-humidifier', + '200s-humidifier4321', ), }), 'is_new': False, diff --git a/tests/components/vesync/snapshots/test_sensor.ambr b/tests/components/vesync/snapshots/test_sensor.ambr index 2525dcd642e..ca7a5cf3ea6 100644 --- a/tests/components/vesync/snapshots/test_sensor.ambr +++ b/tests/components/vesync/snapshots/test_sensor.ambr @@ -664,7 +664,7 @@ 'identifiers': set({ tuple( 'vesync', - '200s-humidifier', + '200s-humidifier4321', ), }), 'is_new': False, @@ -715,7 +715,7 @@ 'previous_unique_id': None, 'supported_features': 0, 'translation_key': None, - 'unique_id': '200s-humidifier-humidity', + 'unique_id': '200s-humidifier4321-humidity', 'unit_of_measurement': '%', }), ]) diff --git a/tests/components/vesync/snapshots/test_switch.ambr b/tests/components/vesync/snapshots/test_switch.ambr index 0a72bb3ca47..ec9cbc4398c 100644 --- a/tests/components/vesync/snapshots/test_switch.ambr +++ b/tests/components/vesync/snapshots/test_switch.ambr @@ -242,7 +242,7 @@ 'identifiers': set({ tuple( 'vesync', - '200s-humidifier', + '200s-humidifier4321', ), }), 'is_new': False, diff --git a/tests/components/vesync/test_humidifier.py b/tests/components/vesync/test_humidifier.py index 3b89ba8e742..b93c97baab6 100644 --- a/tests/components/vesync/test_humidifier.py +++ b/tests/components/vesync/test_humidifier.py @@ -1,6 +1,7 @@ """Tests for the humidifier platform.""" from contextlib import nullcontext +import logging from unittest.mock import patch import pytest @@ -12,7 +13,7 @@ from homeassistant.components.humidifier import ( SERVICE_SET_HUMIDITY, SERVICE_SET_MODE, ) -from homeassistant.config_entries import ConfigEntryState +from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TURN_OFF, @@ -21,6 +22,7 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError, ServiceValidationError +from homeassistant.helpers import entity_registry as er from .common import ( ENTITY_HUMIDIFIER, @@ -225,3 +227,61 @@ async def test_set_mode( ) await hass.async_block_till_done() method_mock.assert_called_once() + + +async def test_base_unique_id( + hass: HomeAssistant, + humidifier_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test that unique_id is based on subDeviceNo.""" + # vesync-device.json defines subDeviceNo for 200s-humidifier as 4321. + entity = entity_registry.async_get(ENTITY_HUMIDIFIER) + assert entity.unique_id.endswith("4321") + + +async def test_invalid_mist_modes( + hass: HomeAssistant, + config_entry: ConfigEntry, + humidifier, + manager, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test unsupported mist mode.""" + + humidifier.mist_modes = ["invalid_mode"] + + with patch( + "homeassistant.components.vesync.async_generate_device_list", + return_value=[humidifier], + ): + caplog.clear() + caplog.set_level(logging.WARNING) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert "Unknown mode 'invalid_mode'" in caplog.text + + +async def test_valid_mist_modes( + hass: HomeAssistant, + config_entry: ConfigEntry, + humidifier, + manager, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test supported mist mode.""" + + humidifier.mist_modes = ["auto", "manual"] + + with patch( + "homeassistant.components.vesync.async_generate_device_list", + return_value=[humidifier], + ): + caplog.clear() + caplog.set_level(logging.WARNING) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + assert "Unknown mode 'auto'" not in caplog.text + assert "Unknown mode 'manual'" not in caplog.text diff --git a/tests/components/vesync/test_init.py b/tests/components/vesync/test_init.py index 3b0df128240..7873b911f6f 100644 --- a/tests/components/vesync/test_init.py +++ b/tests/components/vesync/test_init.py @@ -90,23 +90,36 @@ async def test_async_setup_entry__loads_fans( assert hass.data[DOMAIN][VS_DEVICES] == [fan] -async def test_async_new_device_discovery__loads_fans( - hass: HomeAssistant, config_entry: ConfigEntry, manager: VeSync, fan +async def test_async_new_device_discovery( + hass: HomeAssistant, config_entry: ConfigEntry, manager: VeSync, fan, humidifier ) -> None: - """Test setup connects to vesync and loads fan as an update call.""" + """Test new device discovery.""" assert await hass.config_entries.async_setup(config_entry.entry_id) # Assert platforms loaded await hass.async_block_till_done() assert config_entry.state is ConfigEntryState.LOADED assert not hass.data[DOMAIN][VS_DEVICES] - fans = [fan] - manager.fans = fans - manager._dev_list = { - "fans": fans, - } - await hass.services.async_call(DOMAIN, SERVICE_UPDATE_DEVS, {}, blocking=True) - assert manager.login.call_count == 1 - assert hass.data[DOMAIN][VS_MANAGER] == manager - assert hass.data[DOMAIN][VS_DEVICES] == [fan] + # Mock discovery of new fan which would get added to VS_DEVICES. + with patch( + "homeassistant.components.vesync.async_generate_device_list", + return_value=[fan], + ): + await hass.services.async_call(DOMAIN, SERVICE_UPDATE_DEVS, {}, blocking=True) + + assert manager.login.call_count == 1 + assert hass.data[DOMAIN][VS_MANAGER] == manager + assert hass.data[DOMAIN][VS_DEVICES] == [fan] + + # Mock discovery of new humidifier which would invoke discovery in all platforms. + # The mocked humidifier needs to have all properties populated for correct processing. + with patch( + "homeassistant.components.vesync.async_generate_device_list", + return_value=[humidifier], + ): + await hass.services.async_call(DOMAIN, SERVICE_UPDATE_DEVS, {}, blocking=True) + + assert manager.login.call_count == 1 + assert hass.data[DOMAIN][VS_MANAGER] == manager + assert hass.data[DOMAIN][VS_DEVICES] == [fan, humidifier]