From c834944ee7f1b3c566c1d82a78111f8ced512c71 Mon Sep 17 00:00:00 2001
From: David Bonnes <zxdavb@bonnes.me>
Date: Fri, 7 Mar 2025 12:04:04 +0000
Subject: [PATCH] Fix evohome to gracefully handle null schedules (#140036)

* extend tests to catch null schedules

* add fixture with null schedule

* remove null schedules for now

* fic the typing for _schedule attr (is list, not dict)

* add valid schedule to fixture

* update ssetpoints only if there is a schedule

* snapshot to match last change

* refactor: dont update switchpoints if no schedule

* add in warnings for null schedules

* add fixture for DHW without schedule
---
 .../components/evohome/coordinator.py         |  12 +-
 homeassistant/components/evohome/entity.py    |  10 +-
 tests/components/evohome/conftest.py          |  12 +-
 tests/components/evohome/const.py             |   3 +-
 .../fixtures/botched/schedule_3933910.json    |   3 +
 .../fixtures/h139906/schedule_3454854.json    |   3 +
 .../fixtures/h139906/schedule_3454855.json    | 143 +++++++++++++
 .../fixtures/h139906/status_2727366.json      |  52 +++++
 .../fixtures/h139906/user_locations.json      | 125 ++++++++++++
 .../evohome/snapshots/test_climate.ambr       | 188 ++++++++++++++++++
 .../evohome/snapshots/test_init.ambr          |   3 +
 .../evohome/snapshots/test_water_heater.ambr  |  10 +
 tests/components/evohome/test_water_heater.py |   2 +-
 13 files changed, 553 insertions(+), 13 deletions(-)
 create mode 100644 tests/components/evohome/fixtures/botched/schedule_3933910.json
 create mode 100644 tests/components/evohome/fixtures/h139906/schedule_3454854.json
 create mode 100644 tests/components/evohome/fixtures/h139906/schedule_3454855.json
 create mode 100644 tests/components/evohome/fixtures/h139906/status_2727366.json
 create mode 100644 tests/components/evohome/fixtures/h139906/user_locations.json

diff --git a/homeassistant/components/evohome/coordinator.py b/homeassistant/components/evohome/coordinator.py
index 3264af6b2fd..33af90089a4 100644
--- a/homeassistant/components/evohome/coordinator.py
+++ b/homeassistant/components/evohome/coordinator.py
@@ -207,10 +207,18 @@ class EvoDataUpdateCoordinator(DataUpdateCoordinator):
 
     async def _update_v2_schedules(self) -> None:
         for zone in self.tcs.zones:
-            await zone.get_schedule()
+            try:
+                await zone.get_schedule()
+            except ec2.InvalidScheduleError as err:
+                self.logger.warning(
+                    "Zone '%s' has an invalid/missing schedule: %r", zone.name, err
+                )
 
         if dhw := self.tcs.hotwater:
-            await dhw.get_schedule()
+            try:
+                await dhw.get_schedule()
+            except ec2.InvalidScheduleError as err:
+                self.logger.warning("DHW has an invalid/missing schedule: %r", err)
 
     async def _async_update_data(self) -> EvoLocStatusResponseT:  # type: ignore[override]
         """Fetch the latest state of an entire TCC Location.
diff --git a/homeassistant/components/evohome/entity.py b/homeassistant/components/evohome/entity.py
index 11215dd47b6..2f93f0fb143 100644
--- a/homeassistant/components/evohome/entity.py
+++ b/homeassistant/components/evohome/entity.py
@@ -6,6 +6,7 @@ import logging
 from typing import Any
 
 import evohomeasync2 as evo
+from evohomeasync2.schemas.typedefs import DayOfWeekDhwT
 
 from homeassistant.core import callback
 from homeassistant.helpers.dispatcher import async_dispatcher_connect
@@ -102,7 +103,7 @@ class EvoChild(EvoEntity):
 
         self._evo_tcs = evo_device.tcs
 
-        self._schedule: dict[str, Any] | None = None
+        self._schedule: list[DayOfWeekDhwT] | None = None
         self._setpoints: dict[str, Any] = {}
 
     @property
@@ -123,6 +124,9 @@ class EvoChild(EvoEntity):
         Only Zones & DHW controllers (but not the TCS) can have schedules.
         """
 
+        if not self._schedule:
+            return self._setpoints
+
         this_sp_dtm, this_sp_val = self._evo_device.this_switchpoint
         next_sp_dtm, next_sp_val = self._evo_device.next_switchpoint
 
@@ -152,10 +156,10 @@ class EvoChild(EvoEntity):
                     self._evo_device,
                     err,
                 )
-                self._schedule = {}
+                self._schedule = []
                 return
             else:
-                self._schedule = schedule or {}  # mypy hint
+                self._schedule = schedule  # type: ignore[assignment]
 
             _LOGGER.debug("Schedule['%s'] = %s", self.name, schedule)
 
diff --git a/tests/components/evohome/conftest.py b/tests/components/evohome/conftest.py
index 5f60bc418e3..313982e3f97 100644
--- a/tests/components/evohome/conftest.py
+++ b/tests/components/evohome/conftest.py
@@ -48,18 +48,18 @@ def location_status_fixture(install: str, loc_id: str | None = None) -> JsonObje
     return load_json_object_fixture(f"{install}/status_{loc_id}.json", DOMAIN)
 
 
-def dhw_schedule_fixture(install: str) -> JsonObjectType:
+def dhw_schedule_fixture(install: str, dhw_id: str | None = None) -> JsonObjectType:
     """Load JSON for the schedule of a domesticHotWater zone."""
     try:
-        return load_json_object_fixture(f"{install}/schedule_dhw.json", DOMAIN)
+        return load_json_object_fixture(f"{install}/schedule_{dhw_id}.json", DOMAIN)
     except FileNotFoundError:
         return load_json_object_fixture("default/schedule_dhw.json", DOMAIN)
 
 
-def zone_schedule_fixture(install: str) -> JsonObjectType:
+def zone_schedule_fixture(install: str, zon_id: str | None = None) -> JsonObjectType:
     """Load JSON for the schedule of a temperatureZone zone."""
     try:
-        return load_json_object_fixture(f"{install}/schedule_zone.json", DOMAIN)
+        return load_json_object_fixture(f"{install}/schedule_{zon_id}.json", DOMAIN)
     except FileNotFoundError:
         return load_json_object_fixture("default/schedule_zone.json", DOMAIN)
 
@@ -120,9 +120,9 @@ def mock_make_request(install: str) -> Callable:
 
         elif "schedule" in url:
             if url.startswith("domesticHotWater"):  # /v2/domesticHotWater/{id}/schedule
-                return dhw_schedule_fixture(install)
+                return dhw_schedule_fixture(install, url[16:23])
             if url.startswith("temperatureZone"):  # /v2/temperatureZone/{id}/schedule
-                return zone_schedule_fixture(install)
+                return zone_schedule_fixture(install, url[16:23])
 
         pytest.fail(f"Unexpected request: {HTTPMethod.GET} {url}")
 
diff --git a/tests/components/evohome/const.py b/tests/components/evohome/const.py
index c3dc92c3fbc..dceb2f60a06 100644
--- a/tests/components/evohome/const.py
+++ b/tests/components/evohome/const.py
@@ -15,8 +15,9 @@ TEST_INSTALLS: Final = (
     "default",  # evohome: multi-zone, with DHW
     "h032585",  # VisionProWifi: no preset modes for TCS, zoneId=systemId
     "h099625",  # RoundThermostat
+    "h139906",  # zone with null schedule
     "sys_004",  # RoundModulation
 )
 #   "botched",  # as default: but with activeFaults, ghost zones & unknown types
 
-TEST_INSTALLS_WITH_DHW: Final = ("default",)
+TEST_INSTALLS_WITH_DHW: Final = ("default", "botched")
diff --git a/tests/components/evohome/fixtures/botched/schedule_3933910.json b/tests/components/evohome/fixtures/botched/schedule_3933910.json
new file mode 100644
index 00000000000..0e5a9308d5b
--- /dev/null
+++ b/tests/components/evohome/fixtures/botched/schedule_3933910.json
@@ -0,0 +1,3 @@
+{
+  "dailySchedules": []
+}
diff --git a/tests/components/evohome/fixtures/h139906/schedule_3454854.json b/tests/components/evohome/fixtures/h139906/schedule_3454854.json
new file mode 100644
index 00000000000..0e5a9308d5b
--- /dev/null
+++ b/tests/components/evohome/fixtures/h139906/schedule_3454854.json
@@ -0,0 +1,3 @@
+{
+  "dailySchedules": []
+}
diff --git a/tests/components/evohome/fixtures/h139906/schedule_3454855.json b/tests/components/evohome/fixtures/h139906/schedule_3454855.json
new file mode 100644
index 00000000000..12f8a6cb390
--- /dev/null
+++ b/tests/components/evohome/fixtures/h139906/schedule_3454855.json
@@ -0,0 +1,143 @@
+{
+  "dailySchedules": [
+    {
+      "dayOfWeek": "Monday",
+      "switchpoints": [
+        {
+          "heatSetpoint": 22.0,
+          "timeOfDay": "05:30:00"
+        },
+        {
+          "heatSetpoint": 20.0,
+          "timeOfDay": "08:00:00"
+        },
+        {
+          "heatSetpoint": 22.5,
+          "timeOfDay": "16:00:00"
+        },
+        {
+          "heatSetpoint": 15.0,
+          "timeOfDay": "23:00:00"
+        }
+      ]
+    },
+    {
+      "dayOfWeek": "Tuesday",
+      "switchpoints": [
+        {
+          "heatSetpoint": 22.0,
+          "timeOfDay": "05:30:00"
+        },
+        {
+          "heatSetpoint": 20.0,
+          "timeOfDay": "08:00:00"
+        },
+        {
+          "heatSetpoint": 22.5,
+          "timeOfDay": "16:00:00"
+        },
+        {
+          "heatSetpoint": 15.0,
+          "timeOfDay": "23:00:00"
+        }
+      ]
+    },
+    {
+      "dayOfWeek": "Wednesday",
+      "switchpoints": [
+        {
+          "heatSetpoint": 22.0,
+          "timeOfDay": "05:30:00"
+        },
+        {
+          "heatSetpoint": 20.0,
+          "timeOfDay": "08:00:00"
+        },
+        {
+          "heatSetpoint": 22.5,
+          "timeOfDay": "12:00:00"
+        },
+        {
+          "heatSetpoint": 15.0,
+          "timeOfDay": "23:00:00"
+        }
+      ]
+    },
+    {
+      "dayOfWeek": "Thursday",
+      "switchpoints": [
+        {
+          "heatSetpoint": 22.0,
+          "timeOfDay": "05:30:00"
+        },
+        {
+          "heatSetpoint": 20.0,
+          "timeOfDay": "08:00:00"
+        },
+        {
+          "heatSetpoint": 22.5,
+          "timeOfDay": "16:00:00"
+        },
+        {
+          "heatSetpoint": 15.0,
+          "timeOfDay": "23:00:00"
+        }
+      ]
+    },
+    {
+      "dayOfWeek": "Friday",
+      "switchpoints": [
+        {
+          "heatSetpoint": 22.0,
+          "timeOfDay": "05:30:00"
+        },
+        {
+          "heatSetpoint": 20.0,
+          "timeOfDay": "08:00:00"
+        },
+        {
+          "heatSetpoint": 22.5,
+          "timeOfDay": "16:00:00"
+        },
+        {
+          "heatSetpoint": 15.0,
+          "timeOfDay": "23:00:00"
+        }
+      ]
+    },
+    {
+      "dayOfWeek": "Saturday",
+      "switchpoints": [
+        {
+          "heatSetpoint": 22.0,
+          "timeOfDay": "07:00:00"
+        },
+        {
+          "heatSetpoint": 22.5,
+          "timeOfDay": "16:00:00"
+        },
+        {
+          "heatSetpoint": 15.0,
+          "timeOfDay": "23:00:00"
+        }
+      ]
+    },
+    {
+      "dayOfWeek": "Sunday",
+      "switchpoints": [
+        {
+          "heatSetpoint": 22.0,
+          "timeOfDay": "07:30:00"
+        },
+        {
+          "heatSetpoint": 22.5,
+          "timeOfDay": "16:00:00"
+        },
+        {
+          "heatSetpoint": 15.0,
+          "timeOfDay": "23:00:00"
+        }
+      ]
+    }
+  ]
+}
diff --git a/tests/components/evohome/fixtures/h139906/status_2727366.json b/tests/components/evohome/fixtures/h139906/status_2727366.json
new file mode 100644
index 00000000000..2c123b796bd
--- /dev/null
+++ b/tests/components/evohome/fixtures/h139906/status_2727366.json
@@ -0,0 +1,52 @@
+{
+  "locationId": "2727366",
+  "gateways": [
+    {
+      "gatewayId": "2513794",
+      "temperatureControlSystems": [
+        {
+          "systemId": "3454856",
+          "zones": [
+            {
+              "zoneId": "3454854",
+              "temperatureStatus": {
+                "temperature": 22.0,
+                "isAvailable": true
+              },
+              "activeFaults": [
+                {
+                  "faultType": "TempZoneSensorCommunicationLost",
+                  "since": "2025-02-06T11:20:29"
+                }
+              ],
+              "setpointStatus": {
+                "targetHeatTemperature": 5.0,
+                "setpointMode": "FollowSchedule"
+              },
+              "name": "Thermostat"
+            },
+            {
+              "zoneId": "3454855",
+              "temperatureStatus": {
+                "temperature": 22.0,
+                "isAvailable": true
+              },
+              "activeFaults": [],
+              "setpointStatus": {
+                "targetHeatTemperature": 20.0,
+                "setpointMode": "FollowSchedule"
+              },
+              "name": "Thermostat 2"
+            }
+          ],
+          "activeFaults": [],
+          "systemModeStatus": {
+            "mode": "Auto",
+            "isPermanent": true
+          }
+        }
+      ],
+      "activeFaults": []
+    }
+  ]
+}
diff --git a/tests/components/evohome/fixtures/h139906/user_locations.json b/tests/components/evohome/fixtures/h139906/user_locations.json
new file mode 100644
index 00000000000..14db65a5e0d
--- /dev/null
+++ b/tests/components/evohome/fixtures/h139906/user_locations.json
@@ -0,0 +1,125 @@
+[
+  {
+    "locationInfo": {
+      "locationId": "2727366",
+      "name": "Vr**********",
+      "streetAddress": "********** *",
+      "city": "*********",
+      "country": "Netherlands",
+      "postcode": "******",
+      "locationType": "Residential",
+      "useDaylightSaveSwitching": true,
+      "timeZone": {
+        "timeZoneId": "WEuropeStandardTime",
+        "displayName": "(UTC+01:00) Amsterdam, Berlijn, Bern, Rome, Stockholm, Wenen",
+        "offsetMinutes": 60,
+        "currentOffsetMinutes": 60,
+        "supportsDaylightSaving": true
+      },
+      "locationOwner": {
+        "userId": "2276512",
+        "username": "nobody@nowhere.com",
+        "firstname": "Gl***",
+        "lastname": "de*****"
+      }
+    },
+    "gateways": [
+      {
+        "gatewayInfo": {
+          "gatewayId": "2513794",
+          "mac": "************",
+          "crc": "****",
+          "isWiFi": false
+        },
+        "temperatureControlSystems": [
+          {
+            "systemId": "3454856",
+            "modelType": "EvoTouch",
+            "zones": [
+              {
+                "zoneId": "3454854",
+                "modelType": "HeatingZone",
+                "setpointCapabilities": {
+                  "maxHeatSetpoint": 35.0,
+                  "minHeatSetpoint": 5.0,
+                  "valueResolution": 0.5,
+                  "canControlHeat": true,
+                  "canControlCool": false,
+                  "allowedSetpointModes": [
+                    "PermanentOverride",
+                    "FollowSchedule",
+                    "TemporaryOverride"
+                  ],
+                  "maxDuration": "1.00:00:00",
+                  "timingResolution": "00:10:00"
+                },
+                "scheduleCapabilities": {
+                  "maxSwitchpointsPerDay": 6,
+                  "minSwitchpointsPerDay": 1,
+                  "timingResolution": "00:10:00",
+                  "setpointValueResolution": 0.5
+                },
+                "name": "Thermostat",
+                "zoneType": "ZoneTemperatureControl"
+              },
+              {
+                "zoneId": "3454855",
+                "modelType": "RoundWireless",
+                "setpointCapabilities": {
+                  "maxHeatSetpoint": 35.0,
+                  "minHeatSetpoint": 5.0,
+                  "valueResolution": 0.5,
+                  "canControlHeat": true,
+                  "canControlCool": false,
+                  "allowedSetpointModes": [
+                    "PermanentOverride",
+                    "FollowSchedule",
+                    "TemporaryOverride"
+                  ],
+                  "maxDuration": "1.00:00:00",
+                  "timingResolution": "00:10:00"
+                },
+                "scheduleCapabilities": {
+                  "maxSwitchpointsPerDay": 6,
+                  "minSwitchpointsPerDay": 0,
+                  "timingResolution": "00:10:00",
+                  "setpointValueResolution": 0.5
+                },
+                "name": "Thermostat 2",
+                "zoneType": "Thermostat"
+              }
+            ],
+            "allowedSystemModes": [
+              {
+                "systemMode": "Auto",
+                "canBePermanent": true,
+                "canBeTemporary": false
+              },
+              {
+                "systemMode": "AutoWithEco",
+                "canBePermanent": true,
+                "canBeTemporary": true,
+                "maxDuration": "1.00:00:00",
+                "timingResolution": "01:00:00",
+                "timingMode": "Duration"
+              },
+              {
+                "systemMode": "Away",
+                "canBePermanent": true,
+                "canBeTemporary": true,
+                "maxDuration": "99.00:00:00",
+                "timingResolution": "1.00:00:00",
+                "timingMode": "Period"
+              },
+              {
+                "systemMode": "HeatingOff",
+                "canBePermanent": true,
+                "canBeTemporary": false
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+]
diff --git a/tests/components/evohome/snapshots/test_climate.ambr b/tests/components/evohome/snapshots/test_climate.ambr
index 23a15e3f64f..5a6a6bff863 100644
--- a/tests/components/evohome/snapshots/test_climate.ambr
+++ b/tests/components/evohome/snapshots/test_climate.ambr
@@ -29,6 +29,16 @@
     ),
   ])
 # ---
+# name: test_ctl_set_hvac_mode[h139906]
+  list([
+    tuple(
+      <SystemMode.HEATING_OFF: 'HeatingOff'>,
+    ),
+    tuple(
+      <SystemMode.AUTO: 'Auto'>,
+    ),
+  ])
+# ---
 # name: test_ctl_set_hvac_mode[minimal]
   list([
     tuple(
@@ -70,6 +80,13 @@
     ),
   ])
 # ---
+# name: test_ctl_turn_off[h139906]
+  list([
+    tuple(
+      <SystemMode.HEATING_OFF: 'HeatingOff'>,
+    ),
+  ])
+# ---
 # name: test_ctl_turn_off[minimal]
   list([
     tuple(
@@ -105,6 +122,13 @@
     ),
   ])
 # ---
+# name: test_ctl_turn_on[h139906]
+  list([
+    tuple(
+      <SystemMode.AUTO: 'Auto'>,
+    ),
+  ])
+# ---
 # name: test_ctl_turn_on[minimal]
   list([
     tuple(
@@ -1118,6 +1142,136 @@
     'state': 'heat',
   })
 # ---
+# name: test_setup_platform[h139906][climate.thermostat-state]
+  StateSnapshot({
+    'attributes': ReadOnlyDict({
+      'current_temperature': 22.0,
+      'friendly_name': 'Thermostat',
+      'hvac_modes': list([
+        <HVACMode.OFF: 'off'>,
+        <HVACMode.HEAT: 'heat'>,
+      ]),
+      'max_temp': 35.0,
+      'min_temp': 5.0,
+      'preset_mode': 'none',
+      'preset_modes': list([
+        'none',
+        'temporary',
+        'permanent',
+      ]),
+      'status': dict({
+        'activeFaults': tuple(
+          dict({
+            'fault_type': 'TempZoneSensorCommunicationLost',
+            'since': '2025-02-06T11:20:29+01:00',
+          }),
+        ),
+        'setpoint_status': dict({
+          'setpoint_mode': 'FollowSchedule',
+          'target_heat_temperature': 5.0,
+        }),
+        'setpoints': dict({
+        }),
+        'temperature_status': dict({
+          'is_available': True,
+          'temperature': 22.0,
+        }),
+        'zone_id': '3454854',
+      }),
+      'supported_features': <ClimateEntityFeature: 401>,
+      'temperature': 5.0,
+    }),
+    'context': <ANY>,
+    'entity_id': 'climate.thermostat',
+    'last_changed': <ANY>,
+    'last_reported': <ANY>,
+    'last_updated': <ANY>,
+    'state': 'off',
+  })
+# ---
+# name: test_setup_platform[h139906][climate.thermostat_2-state]
+  StateSnapshot({
+    'attributes': ReadOnlyDict({
+      'current_temperature': 22.0,
+      'friendly_name': 'Thermostat 2',
+      'hvac_modes': list([
+        <HVACMode.OFF: 'off'>,
+        <HVACMode.HEAT: 'heat'>,
+      ]),
+      'max_temp': 35.0,
+      'min_temp': 5.0,
+      'preset_mode': 'none',
+      'preset_modes': list([
+        'none',
+        'temporary',
+        'permanent',
+      ]),
+      'status': dict({
+        'activeFaults': tuple(
+        ),
+        'setpoint_status': dict({
+          'setpoint_mode': 'FollowSchedule',
+          'target_heat_temperature': 20.0,
+        }),
+        'setpoints': dict({
+          'next_sp_from': HAFakeDatetime(2024, 7, 10, 23, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Berlin')),
+          'next_sp_temp': 15.0,
+          'this_sp_from': HAFakeDatetime(2024, 7, 10, 12, 0, tzinfo=zoneinfo.ZoneInfo(key='Europe/Berlin')),
+          'this_sp_temp': 22.5,
+        }),
+        'temperature_status': dict({
+          'is_available': True,
+          'temperature': 22.0,
+        }),
+        'zone_id': '3454855',
+      }),
+      'supported_features': <ClimateEntityFeature: 401>,
+      'temperature': 20.0,
+    }),
+    'context': <ANY>,
+    'entity_id': 'climate.thermostat_2',
+    'last_changed': <ANY>,
+    'last_reported': <ANY>,
+    'last_updated': <ANY>,
+    'state': 'heat',
+  })
+# ---
+# name: test_setup_platform[h139906][climate.vr-state]
+  StateSnapshot({
+    'attributes': ReadOnlyDict({
+      'current_temperature': 22.0,
+      'friendly_name': 'Vr**********',
+      'hvac_modes': list([
+        <HVACMode.OFF: 'off'>,
+        <HVACMode.HEAT: 'heat'>,
+      ]),
+      'icon': 'mdi:thermostat',
+      'max_temp': 35,
+      'min_temp': 7,
+      'preset_mode': None,
+      'preset_modes': list([
+        'eco',
+        'away',
+      ]),
+      'status': dict({
+        'activeSystemFaults': tuple(
+        ),
+        'system_id': '3454856',
+        'system_mode_status': dict({
+          'is_permanent': True,
+          'mode': 'Auto',
+        }),
+      }),
+      'supported_features': <ClimateEntityFeature: 400>,
+    }),
+    'context': <ANY>,
+    'entity_id': 'climate.vr',
+    'last_changed': <ANY>,
+    'last_reported': <ANY>,
+    'last_updated': <ANY>,
+    'state': 'heat',
+  })
+# ---
 # name: test_setup_platform[minimal][climate.main_room-state]
   StateSnapshot({
     'attributes': ReadOnlyDict({
@@ -1312,6 +1466,13 @@
     ),
   ])
 # ---
+# name: test_zone_set_hvac_mode[h139906]
+  list([
+    tuple(
+      5.0,
+    ),
+  ])
+# ---
 # name: test_zone_set_hvac_mode[minimal]
   list([
     tuple(
@@ -1365,6 +1526,19 @@
     }),
   ])
 # ---
+# name: test_zone_set_preset_mode[h139906]
+  list([
+    tuple(
+      5.0,
+    ),
+    tuple(
+      5.0,
+    ),
+    dict({
+      'until': None,
+    }),
+  ])
+# ---
 # name: test_zone_set_preset_mode[minimal]
   list([
     tuple(
@@ -1412,6 +1586,13 @@
     }),
   ])
 # ---
+# name: test_zone_set_temperature[h139906]
+  list([
+    dict({
+      'until': None,
+    }),
+  ])
+# ---
 # name: test_zone_set_temperature[minimal]
   list([
     dict({
@@ -1447,6 +1628,13 @@
     ),
   ])
 # ---
+# name: test_zone_turn_off[h139906]
+  list([
+    tuple(
+      5.0,
+    ),
+  ])
+# ---
 # name: test_zone_turn_off[minimal]
   list([
     tuple(
diff --git a/tests/components/evohome/snapshots/test_init.ambr b/tests/components/evohome/snapshots/test_init.ambr
index d2e91e3c43d..d6174a53356 100644
--- a/tests/components/evohome/snapshots/test_init.ambr
+++ b/tests/components/evohome/snapshots/test_init.ambr
@@ -11,6 +11,9 @@
 # name: test_setup[h099625]
   dict_keys(['refresh_system', 'set_system_mode', 'clear_zone_override', 'set_zone_override'])
 # ---
+# name: test_setup[h139906]
+  dict_keys(['refresh_system', 'set_system_mode', 'clear_zone_override', 'set_zone_override'])
+# ---
 # name: test_setup[minimal]
   dict_keys(['refresh_system', 'reset_system', 'set_system_mode', 'clear_zone_override', 'set_zone_override'])
 # ---
diff --git a/tests/components/evohome/snapshots/test_water_heater.ambr b/tests/components/evohome/snapshots/test_water_heater.ambr
index 771e2c20cba..7b1bc44550a 100644
--- a/tests/components/evohome/snapshots/test_water_heater.ambr
+++ b/tests/components/evohome/snapshots/test_water_heater.ambr
@@ -1,4 +1,14 @@
 # serializer version: 1
+# name: test_set_operation_mode[botched]
+  list([
+    dict({
+      'until': HAFakeDatetime(2024, 7, 10, 12, 0, tzinfo=datetime.timezone.utc),
+    }),
+    dict({
+      'until': HAFakeDatetime(2024, 7, 10, 12, 0, tzinfo=datetime.timezone.utc),
+    }),
+  ])
+# ---
 # name: test_set_operation_mode[default]
   list([
     dict({
diff --git a/tests/components/evohome/test_water_heater.py b/tests/components/evohome/test_water_heater.py
index a201ff63d1e..ca9a5ba6af8 100644
--- a/tests/components/evohome/test_water_heater.py
+++ b/tests/components/evohome/test_water_heater.py
@@ -33,7 +33,7 @@ from .const import TEST_INSTALLS_WITH_DHW
 DHW_ENTITY_ID = "water_heater.domestic_hot_water"
 
 
-@pytest.mark.parametrize("install", [*TEST_INSTALLS_WITH_DHW, "botched"])
+@pytest.mark.parametrize("install", TEST_INSTALLS_WITH_DHW)
 async def test_setup_platform(
     hass: HomeAssistant,
     config: dict[str, str],
-- 
GitLab