diff --git a/tests/components/evohome/snapshots/test_climate.ambr b/tests/components/evohome/snapshots/test_climate.ambr
new file mode 100644
index 0000000000000000000000000000000000000000..1a77cf0e80de6a20d7032d778d1613e9b7cb9f94
--- /dev/null
+++ b/tests/components/evohome/snapshots/test_climate.ambr
@@ -0,0 +1,190 @@
+# serializer version: 1
+# name: test_zone_set_hvac_mode[default]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 5.0,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_hvac_mode[h032585]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 4.5,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_hvac_mode[h099625]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 5.0,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_hvac_mode[minimal]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 5.0,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_hvac_mode[sys_004]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 5.0,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_preset_mode[default]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 17.0,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+    tuple(
+      dict({
+        'HeatSetpointValue': 17.0,
+        'setpointMode': <ZoneMode.TEMPORARY_OVERRIDE: 'TemporaryOverride'>,
+        'timeUntil': '2024-07-10T21:10:00Z',
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_preset_mode[h032585]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 21.5,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+    tuple(
+      dict({
+        'HeatSetpointValue': 21.5,
+        'setpointMode': <ZoneMode.TEMPORARY_OVERRIDE: 'TemporaryOverride'>,
+        'timeUntil': '2024-07-10T21:10:00Z',
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_preset_mode[h099625]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 21.5,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+    tuple(
+      dict({
+        'HeatSetpointValue': 21.5,
+        'setpointMode': <ZoneMode.TEMPORARY_OVERRIDE: 'TemporaryOverride'>,
+        'timeUntil': '2024-07-10T19:10:00Z',
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_preset_mode[minimal]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 17.0,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+    tuple(
+      dict({
+        'HeatSetpointValue': 17.0,
+        'setpointMode': <ZoneMode.TEMPORARY_OVERRIDE: 'TemporaryOverride'>,
+        'timeUntil': '2024-07-10T21:10:00Z',
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_preset_mode[sys_004]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 15.0,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+    tuple(
+      dict({
+        'HeatSetpointValue': 15.0,
+        'setpointMode': <ZoneMode.TEMPORARY_OVERRIDE: 'TemporaryOverride'>,
+        'timeUntil': '2024-07-10T20:10:00Z',
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_temperature[default]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 19.1,
+        'setpointMode': <ZoneMode.TEMPORARY_OVERRIDE: 'TemporaryOverride'>,
+        'timeUntil': '2024-07-10T21:10:00Z',
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_temperature[h032585]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 19.1,
+        'setpointMode': <ZoneMode.TEMPORARY_OVERRIDE: 'TemporaryOverride'>,
+        'timeUntil': '2024-07-10T21:10:00Z',
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_temperature[h099625]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 19.1,
+        'setpointMode': <ZoneMode.TEMPORARY_OVERRIDE: 'TemporaryOverride'>,
+        'timeUntil': '2024-07-10T19:10:00Z',
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_temperature[minimal]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 19.1,
+        'setpointMode': <ZoneMode.TEMPORARY_OVERRIDE: 'TemporaryOverride'>,
+        'timeUntil': '2024-07-10T21:10:00Z',
+      }),
+    ),
+  ])
+# ---
+# name: test_zone_set_temperature[sys_004]
+  list([
+    tuple(
+      dict({
+        'HeatSetpointValue': 19.1,
+        'setpointMode': <ZoneMode.PERMANENT_OVERRIDE: 'PermanentOverride'>,
+      }),
+    ),
+  ])
+# ---
diff --git a/tests/components/evohome/test_climate.py b/tests/components/evohome/test_climate.py
new file mode 100644
index 0000000000000000000000000000000000000000..602a2ac561a982ce288206faad8138780a3fe77a
--- /dev/null
+++ b/tests/components/evohome/test_climate.py
@@ -0,0 +1,203 @@
+"""The tests for climate entities of evohome.
+
+All evohome systems have controllers and at least one zone.
+"""
+
+from __future__ import annotations
+
+from unittest.mock import patch
+
+from freezegun.api import FrozenDateTimeFactory
+import pytest
+from syrupy import SnapshotAssertion
+
+from homeassistant.components.climate import HVACMode
+from homeassistant.components.evohome import DOMAIN
+from homeassistant.components.evohome.climate import EvoZone
+from homeassistant.components.evohome.coordinator import EvoBroker
+from homeassistant.const import Platform
+from homeassistant.core import HomeAssistant
+from homeassistant.helpers import entity_registry as er
+from homeassistant.helpers.entity_component import EntityComponent
+from homeassistant.util import dt as dt_util
+
+from .conftest import setup_evohome
+from .const import TEST_INSTALLS
+
+
+def get_zone_entity(hass: HomeAssistant) -> EvoZone:
+    """Return the entity of the first zone of the evohome system."""
+
+    broker: EvoBroker = hass.data[DOMAIN]["broker"]
+
+    unique_id = broker.tcs._zones[0]._id
+    if unique_id == broker.tcs._id:
+        unique_id += "z"  # special case of merged controller/zone
+
+    entity_registry = er.async_get(hass)
+    entity_id = entity_registry.async_get_entity_id(Platform.CLIMATE, DOMAIN, unique_id)
+
+    component: EntityComponent = hass.data.get(Platform.CLIMATE)  # type: ignore[assignment]
+    return next(e for e in component.entities if e.entity_id == entity_id)  # type: ignore[return-value]
+
+
+@pytest.mark.parametrize("install", TEST_INSTALLS)
+async def test_zone_set_hvac_mode(
+    hass: HomeAssistant,
+    config: dict[str, str],
+    install: str,
+    snapshot: SnapshotAssertion,
+) -> None:
+    """Test climate methods of a evohome-compatible zone."""
+
+    results = []
+
+    async for _ in setup_evohome(hass, config, install=install):
+        zone = get_zone_entity(hass)
+
+        assert zone.hvac_modes == [HVACMode.OFF, HVACMode.HEAT]
+
+        # set_hvac_mode(HVACMode.HEAT): FollowSchedule
+        with patch("evohomeasync2.zone.Zone._set_mode") as mock_fcn:
+            await zone.async_set_hvac_mode(HVACMode.HEAT)
+
+            assert mock_fcn.await_count == 1
+            assert install != "default" or mock_fcn.await_args.args == (
+                {
+                    "setpointMode": "FollowSchedule",
+                },
+            )
+            assert mock_fcn.await_args.kwargs == {}
+
+        # set_hvac_mode(HVACMode.OFF): PermanentOverride, minHeatSetpoint
+        with patch("evohomeasync2.zone.Zone._set_mode") as mock_fcn:
+            await zone.async_set_hvac_mode(HVACMode.OFF)
+
+            assert mock_fcn.await_count == 1
+            assert install != "default" or mock_fcn.await_args.args == (
+                {
+                    "setpointMode": "PermanentOverride",
+                    "HeatSetpointValue": 5.0,  # varies by install
+                },
+            )
+            assert mock_fcn.await_args.kwargs == {}
+
+            results.append(mock_fcn.await_args.args)
+
+    assert results == snapshot
+
+
+@pytest.mark.parametrize("install", TEST_INSTALLS)
+async def test_zone_set_preset_mode(
+    hass: HomeAssistant,
+    config: dict[str, str],
+    install: str,
+    freezer: FrozenDateTimeFactory,
+    snapshot: SnapshotAssertion,
+) -> None:
+    """Test climate methods of a evohome-compatible zone."""
+
+    freezer.move_to("2024-07-10T12:00:00Z")
+    results = []
+
+    async for _ in setup_evohome(hass, config, install=install):
+        zone = get_zone_entity(hass)
+
+        assert zone.preset_modes == ["none", "temporary", "permanent"]
+
+        # set_preset_mode(none): FollowSchedule
+        with patch("evohomeasync2.zone.Zone._set_mode") as mock_fcn:
+            await zone.async_set_preset_mode("none")
+
+            assert mock_fcn.await_count == 1
+            assert mock_fcn.await_args.args == (
+                {
+                    "setpointMode": "FollowSchedule",
+                },
+            )
+            assert mock_fcn.await_args.kwargs == {}
+
+        # set_preset_mode(permanent): PermanentOverride
+        with patch("evohomeasync2.zone.Zone._set_mode") as mock_fcn:
+            await zone.async_set_preset_mode("permanent")
+
+            assert mock_fcn.await_count == 1
+            assert install != "default" or mock_fcn.await_args.args == (
+                {
+                    "setpointMode": "PermanentOverride",
+                    "HeatSetpointValue": 17.0,  # varies by install
+                },
+            )
+            assert mock_fcn.await_args.kwargs == {}
+
+            results.append(mock_fcn.await_args.args)
+
+        # set_preset_mode(permanent): TemporaryOverride
+        with patch("evohomeasync2.zone.Zone._set_mode") as mock_fcn:
+            await zone.async_set_preset_mode("temporary")
+
+            assert mock_fcn.await_count == 1
+            assert install != "default" or mock_fcn.await_args.args == (
+                {
+                    "setpointMode": "TemporaryOverride",
+                    "HeatSetpointValue": 17.0,  # varies by install
+                    "timeUntil": "2024-07-10T21:10:00Z",  # varies by install
+                },
+            )
+            assert mock_fcn.await_args.kwargs == {}
+
+            results.append(mock_fcn.await_args.args)
+
+    assert results == snapshot
+
+
+@pytest.mark.parametrize("install", TEST_INSTALLS)
+async def test_zone_set_temperature(
+    hass: HomeAssistant,
+    config: dict[str, str],
+    install: str,
+    freezer: FrozenDateTimeFactory,
+    snapshot: SnapshotAssertion,
+) -> None:
+    """Test climate methods of a evohome-compatible zone."""
+
+    freezer.move_to("2024-07-10T12:00:00Z")
+    results = []
+
+    async for _ in setup_evohome(hass, config, install=install):
+        zone = get_zone_entity(hass)
+
+        # set_temperature(temp): TemporaryOverride, advanced
+        with patch("evohomeasync2.zone.Zone._set_mode") as mock_fcn:
+            await zone.async_set_temperature(temperature=19.1)
+
+            assert mock_fcn.await_count == 1
+            assert install != "default" or mock_fcn.await_args.args == (
+                {
+                    "setpointMode": "TemporaryOverride",
+                    "HeatSetpointValue": 19.1,
+                    "timeUntil": "2024-07-10T21:10:00Z",  # varies by install
+                },
+            )
+            assert mock_fcn.await_args.kwargs == {}
+
+            results.append(mock_fcn.await_args.args)
+
+        # set_temperature(temp, until): TemporaryOverride, until
+        with patch("evohomeasync2.zone.Zone._set_mode") as mock_fcn:
+            await zone.async_set_temperature(
+                temperature=19.2,
+                until=dt_util.parse_datetime("2024-07-10T13:30:00Z"),
+            )
+
+            assert mock_fcn.await_count == 1
+            assert mock_fcn.await_args.args == (
+                {
+                    "setpointMode": "TemporaryOverride",
+                    "HeatSetpointValue": 19.2,
+                    "timeUntil": "2024-07-10T13:30:00Z",
+                },
+            )
+            assert mock_fcn.await_args.kwargs == {}
+
+    assert results == snapshot