diff --git a/tests/components/tuya/__init__.py b/tests/components/tuya/__init__.py index 7d6cd32959c..369b4b09f14 100644 --- a/tests/components/tuya/__init__.py +++ b/tests/components/tuya/__init__.py @@ -294,6 +294,10 @@ DEVICE_MOCKS = { Platform.BINARY_SENSOR, Platform.SENSOR, ], + "sd_lr33znaodtyarrrz": [ + # https://github.com/home-assistant/core/issues/141278 + Platform.VACUUM, + ], "sfkzq_o6dagifntoafakst": [ # https://github.com/home-assistant/core/issues/148116 Platform.SWITCH, diff --git a/tests/components/tuya/fixtures/sd_lr33znaodtyarrrz.json b/tests/components/tuya/fixtures/sd_lr33znaodtyarrrz.json new file mode 100644 index 00000000000..77d94cb951b --- /dev/null +++ b/tests/components/tuya/fixtures/sd_lr33znaodtyarrrz.json @@ -0,0 +1,476 @@ +{ + "endpoint": "https://apigw.tuyaeu.com", + "terminal_id": "REDACTED", + "mqtt_connected": true, + "disabled_by": null, + "disabled_polling": false, + "id": "bfa951ca98fcf64fddqlmt", + "name": "V20", + "category": "sd", + "product_id": "lr33znaodtyarrrz", + "product_name": "V20", + "online": true, + "sub": false, + "time_zone": "+01:00", + "active_time": "2025-03-23T16:37:02+00:00", + "create_time": "2025-03-23T16:37:02+00:00", + "update_time": "2025-03-23T16:37:02+00:00", + "function": { + "power_go": { + "type": "Boolean", + "value": {} + }, + "pause": { + "type": "Boolean", + "value": {} + }, + "switch_charge": { + "type": "Boolean", + "value": {} + }, + "mode": { + "type": "Enum", + "value": { + "range": ["smart", "zone", "pose", "part"] + } + }, + "suction": { + "type": "Enum", + "value": { + "range": ["gentle", "normal", "strong"] + } + }, + "cistern": { + "type": "Enum", + "value": { + "range": ["low", "middle", "high"] + } + }, + "seek": { + "type": "Boolean", + "value": {} + }, + "direction_control": { + "type": "Enum", + "value": { + "range": ["forward", "turn_left", "turn_right", "stop"] + } + }, + "reset_map": { + "type": "Boolean", + "value": {} + }, + "path_data": { + "type": "Raw", + "value": {} + }, + "command_trans": { + "type": "Raw", + "value": {} + }, + "request": { + "type": "Enum", + "value": { + "range": ["get_map", "get_path", "get_both"] + } + }, + "reset_edge_brush": { + "type": "Boolean", + "value": {} + }, + "reset_roll_brush": { + "type": "Boolean", + "value": {} + }, + "reset_filter": { + "type": "Boolean", + "value": {} + }, + "reset_duster_cloth": { + "type": "Boolean", + "value": {} + }, + "switch_disturb": { + "type": "Boolean", + "value": {} + }, + "volume_set": { + "type": "Integer", + "value": { + "unit": "%", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + }, + "break_clean": { + "type": "Boolean", + "value": {} + }, + "device_timer": { + "type": "Raw", + "value": {} + }, + "disturb_time_set": { + "type": "Raw", + "value": {} + }, + "voice_data": { + "type": "Raw", + "value": {} + }, + "language": { + "type": "Enum", + "value": { + "range": [ + "chinese_simplified", + "chinese_traditional", + "english", + "german", + "french", + "russian", + "spanish", + "korean", + "latin", + "portuguese", + "japanese", + "italian" + ] + } + }, + "customize_mode_switch": { + "type": "Boolean", + "value": {} + } + }, + "status_range": { + "power_go": { + "type": "Boolean", + "value": {} + }, + "pause": { + "type": "Boolean", + "value": {} + }, + "switch_charge": { + "type": "Boolean", + "value": {} + }, + "mode": { + "type": "Enum", + "value": { + "range": ["smart", "zone", "pose", "part"] + } + }, + "status": { + "type": "Enum", + "value": { + "range": [ + "standby", + "zone_clean", + "part_clean", + "cleaning", + "paused", + "goto_pos", + "pos_arrived", + "pos_unarrive", + "goto_charge", + "charging", + "charge_done", + "sleep" + ] + } + }, + "clean_time": { + "type": "Integer", + "value": { + "unit": "min", + "min": 0, + "max": 9999, + "scale": 0, + "step": 1 + } + }, + "clean_area": { + "type": "Integer", + "value": { + "unit": "㎡", + "min": 0, + "max": 9999, + "scale": 0, + "step": 1 + } + }, + "electricity_left": { + "type": "Integer", + "value": { + "unit": "%", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + }, + "suction": { + "type": "Enum", + "value": { + "range": ["closed", "gentle", "normal", "strong"] + } + }, + "cistern": { + "type": "Enum", + "value": { + "range": ["closed", "low", "middle", "high"] + } + }, + "seek": { + "type": "Boolean", + "value": {} + }, + "direction_control": { + "type": "Enum", + "value": { + "range": ["forward", "turn_left", "turn_right", "stop"] + } + }, + "reset_map": { + "type": "Boolean", + "value": {} + }, + "path_data": { + "type": "Raw", + "value": {} + }, + "command_trans": { + "type": "Raw", + "value": {} + }, + "request": { + "type": "Enum", + "value": { + "range": ["get_map", "get_path", "get_both"] + } + }, + "edge_brush": { + "type": "Integer", + "value": { + "unit": "min", + "min": 0, + "max": 9000, + "scale": 0, + "step": 1 + } + }, + "reset_edge_brush": { + "type": "Boolean", + "value": {} + }, + "roll_brush": { + "type": "Integer", + "value": { + "unit": "min", + "min": 0, + "max": 18000, + "scale": 0, + "step": 1 + } + }, + "reset_roll_brush": { + "type": "Boolean", + "value": {} + }, + "filter": { + "type": "Integer", + "value": { + "unit": "min", + "min": 0, + "max": 9000, + "scale": 0, + "step": 1 + } + }, + "reset_filter": { + "type": "Boolean", + "value": {} + }, + "duster_cloth": { + "type": "Integer", + "value": { + "unit": "min", + "min": 0, + "max": 9000, + "scale": 0, + "step": 1 + } + }, + "reset_duster_cloth": { + "type": "Boolean", + "value": {} + }, + "switch_disturb": { + "type": "Boolean", + "value": {} + }, + "volume_set": { + "type": "Integer", + "value": { + "unit": "%", + "min": 0, + "max": 100, + "scale": 0, + "step": 1 + } + }, + "break_clean": { + "type": "Boolean", + "value": {} + }, + "fault": { + "type": "Bitmap", + "value": { + "label": [ + "low_power", + "poweroff", + "wheel_trap", + "cannot_upgrade", + "collision_stuck", + "dust_station_full", + "tile_error", + "lidar_speed_err", + "lidar_cover", + "lidar_point_err", + "front_wall_dirty", + "psd_dirty", + "middle_sweep", + "side_sweep", + "fan_speed", + "dustbox_out", + "dustbox_full", + "no_dust_box", + "dustbox_fullout", + "trapped", + "pick_up", + "no_dust_water_box", + "water_box_empty", + "forbid_area", + "land_check", + "findcharge_fail", + "battery_err", + "kit_wheel", + "kit_lidar", + "kit_water_pump" + ] + } + }, + "total_clean_area": { + "type": "Integer", + "value": { + "unit": "㎡", + "min": 0, + "max": 99999, + "scale": 0, + "step": 1 + } + }, + "total_clean_count": { + "type": "Integer", + "value": { + "min": 0, + "max": 99999, + "scale": 0, + "step": 1 + } + }, + "total_clean_time": { + "type": "Integer", + "value": { + "unit": "min", + "min": 0, + "max": 99999, + "scale": 0, + "step": 1 + } + }, + "device_timer": { + "type": "Raw", + "value": {} + }, + "disturb_time_set": { + "type": "Raw", + "value": {} + }, + "device_info": { + "type": "Raw", + "value": {} + }, + "voice_data": { + "type": "Raw", + "value": {} + }, + "language": { + "type": "Enum", + "value": { + "range": [ + "chinese_simplified", + "chinese_traditional", + "english", + "german", + "french", + "russian", + "spanish", + "korean", + "latin", + "portuguese", + "japanese", + "italian" + ] + } + }, + "customize_mode_switch": { + "type": "Boolean", + "value": {} + } + }, + "status": { + "power_go": false, + "pause": false, + "switch_charge": false, + "mode": "goto_charge", + "status": "charge_done", + "clean_time": 0, + "clean_area": 0, + "electricity_left": 100, + "suction": "strong", + "cistern": "middle", + "seek": false, + "direction_control": "forward", + "reset_map": false, + "path_data": "", + "command_trans": "qgABFxc=", + "request": "get_map", + "edge_brush": 8944, + "reset_edge_brush": false, + "roll_brush": 17948, + "reset_roll_brush": false, + "filter": 8956, + "reset_filter": false, + "duster_cloth": 9000, + "reset_duster_cloth": false, + "switch_disturb": false, + "volume_set": 95, + "break_clean": true, + "fault": 0, + "total_clean_area": 24, + "total_clean_count": 1, + "total_clean_time": 42, + "device_timer": "qgADMQEAMg==", + "disturb_time_set": "qgAIMwEWAAAIAABS", + "device_info": "eyJEZXZpY2VfU04iOiJJRlYyMDI1MDExNTAyMDIwMiIsIkZpcm13YXJlX1ZlcnNpb24iOiIxLjQuMyIsIklQIjoiMTkyLjE2OC4wLjIwMyIsIk1DVV9WZXJzaW9uIjoiMC4zMTQxLjEwNyIsIk1hYyI6IjM0OjE3OjM2OkU1OjAyOjc4IiwiTW9kdWxlX1VVSUQiOiJ6ZjExYjJmNzQ4Mzg5ZTY5ZDk4NiIsIlJTU0kiOiItNTAiLCJXaUZpX05hbWUiOiJGcnl0a2lfemFfZGFybW8ifQ==", + "voice_data": "qwAAAAAHNQAAAAADZJw=", + "language": "chinese_simplified", + "customize_mode_switch": false + }, + "set_up": true, + "support_local": true +} diff --git a/tests/components/tuya/snapshots/test_vacuum.ambr b/tests/components/tuya/snapshots/test_vacuum.ambr new file mode 100644 index 00000000000..0425cc45060 --- /dev/null +++ b/tests/components/tuya/snapshots/test_vacuum.ambr @@ -0,0 +1,64 @@ +# serializer version: 1 +# name: test_platform_setup_and_discovery[sd_lr33znaodtyarrrz][vacuum.v20-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': dict({ + 'fan_speed_list': list([ + 'gentle', + 'normal', + 'strong', + ]), + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'vacuum', + 'entity_category': None, + 'entity_id': 'vacuum.v20', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': None, + 'original_icon': None, + 'original_name': None, + 'platform': 'tuya', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': , + 'translation_key': None, + 'unique_id': 'tuya.bfa951ca98fcf64fddqlmt', + 'unit_of_measurement': None, + }) +# --- +# name: test_platform_setup_and_discovery[sd_lr33znaodtyarrrz][vacuum.v20-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'battery_icon': 'mdi:battery-charging-100', + 'battery_level': 100, + 'fan_speed': 'strong', + 'fan_speed_list': list([ + 'gentle', + 'normal', + 'strong', + ]), + 'friendly_name': 'V20', + 'supported_features': , + }), + 'context': , + 'entity_id': 'vacuum.v20', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'docked', + }) +# --- diff --git a/tests/components/tuya/test_vacuum.py b/tests/components/tuya/test_vacuum.py new file mode 100644 index 00000000000..c3d1f5fe980 --- /dev/null +++ b/tests/components/tuya/test_vacuum.py @@ -0,0 +1,57 @@ +"""Test Tuya vacuum platform.""" + +from __future__ import annotations + +from unittest.mock import patch + +import pytest +from syrupy.assertion import SnapshotAssertion +from tuya_sharing import CustomerDevice + +from homeassistant.components.tuya import ManagerCompat +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import DEVICE_MOCKS, initialize_entry + +from tests.common import MockConfigEntry, snapshot_platform + + +@pytest.mark.parametrize( + "mock_device_code", + [k for k, v in DEVICE_MOCKS.items() if Platform.VACUUM in v], +) +@patch("homeassistant.components.tuya.PLATFORMS", [Platform.VACUUM]) +async def test_platform_setup_and_discovery( + hass: HomeAssistant, + mock_manager: ManagerCompat, + mock_config_entry: MockConfigEntry, + mock_device: CustomerDevice, + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, +) -> None: + """Test platform setup and discovery.""" + await initialize_entry(hass, mock_manager, mock_config_entry, mock_device) + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +@pytest.mark.parametrize( + "mock_device_code", + [k for k, v in DEVICE_MOCKS.items() if Platform.VACUUM not in v], +) +@patch("homeassistant.components.tuya.PLATFORMS", [Platform.VACUUM]) +async def test_platform_setup_no_discovery( + hass: HomeAssistant, + mock_manager: ManagerCompat, + mock_config_entry: MockConfigEntry, + mock_device: CustomerDevice, + entity_registry: er.EntityRegistry, +) -> None: + """Test platform setup without discovery.""" + await initialize_entry(hass, mock_manager, mock_config_entry, mock_device) + + assert not er.async_entries_for_config_entry( + entity_registry, mock_config_entry.entry_id + )