mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 23:21:54 +00:00 
			
		
		
		
	preen
This commit is contained in:
		| @@ -9,6 +9,7 @@ from esphome.const import ( | ||||
|     CONF_COOL_DEADBAND, | ||||
|     CONF_COOL_MODE, | ||||
|     CONF_COOL_OVERRUN, | ||||
|     CONF_CUSTOM_FAN_MODES, | ||||
|     CONF_DEFAULT_MODE, | ||||
|     CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, | ||||
|     CONF_DEFAULT_TARGET_TEMPERATURE_LOW, | ||||
| @@ -658,6 +659,7 @@ CONFIG_SCHEMA = cv.All( | ||||
|                 } | ||||
|             ), | ||||
|             cv.Optional(CONF_PRESET): cv.ensure_list(PRESET_CONFIG_SCHEMA), | ||||
|             cv.Optional(CONF_CUSTOM_FAN_MODES): cv.ensure_list(cv.string_strict), | ||||
|             cv.Optional(CONF_ON_BOOT_RESTORE_FROM): validate_on_boot_restore_from, | ||||
|             cv.Optional(CONF_PRESET_CHANGE): automation.validate_automation( | ||||
|                 single=True | ||||
| @@ -1008,3 +1010,21 @@ async def to_code(config): | ||||
|         await automation.build_automation( | ||||
|             var.get_preset_change_trigger(), [], config[CONF_PRESET_CHANGE] | ||||
|         ) | ||||
|  | ||||
|     # Collect custom preset names from preset map (non-standard preset names only) | ||||
|     custom_preset_names = [ | ||||
|         preset_config[CONF_NAME] | ||||
|         for preset_config in config.get(CONF_PRESET, []) | ||||
|         if preset_config[CONF_NAME].upper() not in climate.CLIMATE_PRESETS | ||||
|     ] | ||||
|     if custom_preset_names: | ||||
|         cg.add(var.set_custom_presets(custom_preset_names)) | ||||
|  | ||||
|     # Collect custom fan modes (filter out standard enum fan modes) | ||||
|     custom_fan_modes = [ | ||||
|         mode | ||||
|         for mode in config.get(CONF_CUSTOM_FAN_MODES, []) | ||||
|         if mode.upper() not in climate.CLIMATE_FAN_MODES | ||||
|     ] | ||||
|     if custom_fan_modes: | ||||
|         cg.add(var.set_custom_fan_modes(custom_fan_modes)) | ||||
|   | ||||
| @@ -0,0 +1,44 @@ | ||||
| esphome: | ||||
|   name: climate-custom-modes-test | ||||
| host: | ||||
| api: | ||||
| logger: | ||||
|  | ||||
| sensor: | ||||
|   - platform: template | ||||
|     id: thermostat_sensor | ||||
|     lambda: "return 22.0;" | ||||
|  | ||||
| climate: | ||||
|   - platform: thermostat | ||||
|     id: test_thermostat | ||||
|     name: Test Thermostat Custom Modes | ||||
|     sensor: thermostat_sensor | ||||
|     preset: | ||||
|       - name: Away | ||||
|         default_target_temperature_low: 16°C | ||||
|         default_target_temperature_high: 20°C | ||||
|       - name: Eco Plus | ||||
|         default_target_temperature_low: 18°C | ||||
|         default_target_temperature_high: 22°C | ||||
|       - name: Super Saver | ||||
|         default_target_temperature_low: 20°C | ||||
|         default_target_temperature_high: 24°C | ||||
|       - name: Vacation Mode | ||||
|         default_target_temperature_low: 15°C | ||||
|         default_target_temperature_high: 18°C | ||||
|     custom_fan_modes: | ||||
|       - "Turbo" | ||||
|       - "Silent" | ||||
|       - "Sleep Mode" | ||||
|     idle_action: | ||||
|       - logger.log: idle_action | ||||
|     cool_action: | ||||
|       - logger.log: cool_action | ||||
|     heat_action: | ||||
|       - logger.log: heat_action | ||||
|     min_cooling_off_time: 10s | ||||
|     min_cooling_run_time: 10s | ||||
|     min_heating_off_time: 10s | ||||
|     min_heating_run_time: 10s | ||||
|     min_idle_time: 10s | ||||
							
								
								
									
										51
									
								
								tests/integration/test_climate_custom_modes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								tests/integration/test_climate_custom_modes.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| """Integration test for climate custom fan modes and presets.""" | ||||
|  | ||||
| from __future__ import annotations | ||||
|  | ||||
| from aioesphomeapi import ClimateInfo, ClimatePreset | ||||
| import pytest | ||||
|  | ||||
| from .types import APIClientConnectedFactory, RunCompiledFunction | ||||
|  | ||||
|  | ||||
| @pytest.mark.asyncio | ||||
| async def test_climate_custom_fan_modes_and_presets( | ||||
|     yaml_config: str, | ||||
|     run_compiled: RunCompiledFunction, | ||||
|     api_client_connected: APIClientConnectedFactory, | ||||
| ) -> None: | ||||
|     """Test that custom fan modes and presets are properly exposed via API.""" | ||||
|     async with run_compiled(yaml_config), api_client_connected() as client: | ||||
|         # Get entities and services | ||||
|         entities, services = await client.list_entities_services() | ||||
|         climate_infos = [e for e in entities if isinstance(e, ClimateInfo)] | ||||
|         assert len(climate_infos) == 1, "Expected exactly 1 climate entity" | ||||
|  | ||||
|         test_climate = climate_infos[0] | ||||
|  | ||||
|         # Verify custom fan modes are exposed | ||||
|         custom_fan_modes = test_climate.supported_custom_fan_modes | ||||
|         assert len(custom_fan_modes) == 3, ( | ||||
|             f"Expected 3 custom fan modes, got {len(custom_fan_modes)}" | ||||
|         ) | ||||
|         assert "Turbo" in custom_fan_modes, "Expected 'Turbo' in custom fan modes" | ||||
|         assert "Silent" in custom_fan_modes, "Expected 'Silent' in custom fan modes" | ||||
|         assert "Sleep Mode" in custom_fan_modes, ( | ||||
|             "Expected 'Sleep Mode' in custom fan modes" | ||||
|         ) | ||||
|  | ||||
|         # Verify enum presets are exposed (from preset: config map) | ||||
|         assert ClimatePreset.AWAY in test_climate.supported_presets, ( | ||||
|             "Expected AWAY in enum presets" | ||||
|         ) | ||||
|  | ||||
|         # Verify custom string presets are exposed (non-standard preset names from preset map) | ||||
|         custom_presets = test_climate.supported_custom_presets | ||||
|         assert len(custom_presets) == 3, ( | ||||
|             f"Expected 3 custom presets, got {len(custom_presets)}: {custom_presets}" | ||||
|         ) | ||||
|         assert "Eco Plus" in custom_presets, "Expected 'Eco Plus' in custom presets" | ||||
|         assert "Comfort" in custom_presets, "Expected 'Comfort' in custom presets" | ||||
|         assert "Vacation Mode" in custom_presets, ( | ||||
|             "Expected 'Vacation Mode' in custom presets" | ||||
|         ) | ||||
		Reference in New Issue
	
	Block a user