From 2450a26eb9ed1f6703e001470d69afae4f4b6d78 Mon Sep 17 00:00:00 2001 From: zerzhang <916250497@qq.com> Date: Fri, 19 Sep 2025 14:15:18 +0800 Subject: [PATCH 1/6] stash --- .../components/switchbot/water_heater.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 homeassistant/components/switchbot/water_heater.py diff --git a/homeassistant/components/switchbot/water_heater.py b/homeassistant/components/switchbot/water_heater.py new file mode 100644 index 00000000000000..a66b41db2bdc59 --- /dev/null +++ b/homeassistant/components/switchbot/water_heater.py @@ -0,0 +1,57 @@ +"""Support for switchbot water heaters.""" + +import logging + +import switchbot + +from homeassistant.components.water_heater import ( + WaterHeaterEntity, + WaterHeaterEntityFeature, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from .coordinator import SwitchbotConfigEntry +from .entity import SwitchbotEntity + +PARALLEL_UPDATES = 0 +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, + entry: SwitchbotConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up Switchbot based on a config entry.""" + coordinator = entry.runtime_data + async_add_entities([SwitchbotThermostat(coordinator)]) + + +class SwitchbotThermostat(SwitchbotEntity, WaterHeaterEntity): + """Representation of a Switchbot Thermostat.""" + + _attr_supported_features = WaterHeaterEntityFeature.TARGET_TEMPERATURE + _device: switchbot.SwitchbotThermostat + _attr_translation_key = "thermostat" + _attr_name = None + + @property + def target_temperature(self) -> float | None: + """Return the temperature we try to reach.""" + return self._device.target_temperature + + @property + def current_temperature(self) -> float | None: + """Return the current temperature.""" + return self._device.current_temperature + + @property + def min_temp(self) -> float: + """Return the minimum temperature.""" + return self._device.min_temp + + @property + def max_temp(self) -> float: + """Return the maximum temperature.""" + return self._device.max_temp From a1db300fbcda67d62748604a85c506aceab35a77 Mon Sep 17 00:00:00 2001 From: zerzhang <916250497@qq.com> Date: Wed, 22 Oct 2025 17:58:54 +0800 Subject: [PATCH 2/6] add support for smart thermostat radiator --- .../components/switchbot/__init__.py | 5 + homeassistant/components/switchbot/climate.py | 144 ++++++++++++++++++ homeassistant/components/switchbot/const.py | 4 + homeassistant/components/switchbot/icons.json | 16 ++ .../components/switchbot/strings.json | 16 ++ .../components/switchbot/water_heater.py | 57 ------- tests/components/switchbot/__init__.py | 23 +++ tests/components/switchbot/test_climate.py | 118 ++++++++++++++ 8 files changed, 326 insertions(+), 57 deletions(-) create mode 100644 homeassistant/components/switchbot/climate.py delete mode 100644 homeassistant/components/switchbot/water_heater.py create mode 100644 tests/components/switchbot/test_climate.py diff --git a/homeassistant/components/switchbot/__init__.py b/homeassistant/components/switchbot/__init__.py index cd8a8b08f1d336..84af2e2761d340 100644 --- a/homeassistant/components/switchbot/__init__.py +++ b/homeassistant/components/switchbot/__init__.py @@ -102,6 +102,10 @@ SupportedModels.RELAY_SWITCH_2PM.value: [Platform.SWITCH, Platform.SENSOR], SupportedModels.GARAGE_DOOR_OPENER.value: [Platform.COVER, Platform.SENSOR], SupportedModels.CLIMATE_PANEL.value: [Platform.SENSOR, Platform.BINARY_SENSOR], + SupportedModels.SMART_THERMOSTAT_RADIATOR.value: [ + Platform.CLIMATE, + Platform.SENSOR, + ], } CLASS_BY_DEVICE = { SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight, @@ -136,6 +140,7 @@ SupportedModels.PLUG_MINI_EU.value: switchbot.SwitchbotRelaySwitch, SupportedModels.RELAY_SWITCH_2PM.value: switchbot.SwitchbotRelaySwitch2PM, SupportedModels.GARAGE_DOOR_OPENER.value: switchbot.SwitchbotGarageDoorOpener, + SupportedModels.SMART_THERMOSTAT_RADIATOR.value: switchbot.SwitchbotSmartThermostatRadiator, } diff --git a/homeassistant/components/switchbot/climate.py b/homeassistant/components/switchbot/climate.py new file mode 100644 index 00000000000000..d5d1db27d1d8c1 --- /dev/null +++ b/homeassistant/components/switchbot/climate.py @@ -0,0 +1,144 @@ +"""Support for Switchbot Climate devices.""" + +from __future__ import annotations + +import logging +from typing import Any + +import switchbot +from switchbot import ( + ClimateAction as SwitchBotClimateAction, + ClimateMode as SwitchBotClimateMode, +) + +from homeassistant.components.climate import ( + ClimateEntity, + ClimateEntityFeature, + HVACAction, + HVACMode, +) +from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from .coordinator import SwitchbotConfigEntry +from .entity import SwitchbotEntity, exception_handler + +SWITCHBOT_CLIMATE_TO_HASS_HVAC_MODE = { + SwitchBotClimateMode.HEAT: HVACMode.HEAT, + SwitchBotClimateMode.OFF: HVACMode.OFF, +} + +HASS_HVAC_MODE_TO_SWITCHBOT_CLIMATE = { + HVACMode.HEAT: SwitchBotClimateMode.HEAT, + HVACMode.OFF: SwitchBotClimateMode.OFF, +} + +SWITCHBOT_ACTION_TO_HASS_HVAC_ACTION = { + SwitchBotClimateAction.HEATING: HVACAction.HEATING, + SwitchBotClimateAction.IDLE: HVACAction.IDLE, + SwitchBotClimateAction.OFF: HVACAction.OFF, +} + +_LOGGER = logging.getLogger(__name__) +PARALLEL_UPDATES = 0 + + +async def async_setup_entry( + hass: HomeAssistant, + entry: SwitchbotConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up Switchbot climate based on a config entry.""" + coordinator = entry.runtime_data + async_add_entities([SwitchBotClimateEntity(coordinator)]) + + +class SwitchBotClimateEntity(SwitchbotEntity, ClimateEntity): + """Representation of a Switchbot Climate device.""" + + _device: switchbot.SwitchbotDevice + _attr_supported_features = ( + ClimateEntityFeature.PRESET_MODE + | ClimateEntityFeature.TARGET_TEMPERATURE + | ClimateEntityFeature.TURN_OFF + | ClimateEntityFeature.TURN_ON + ) + _attr_temperature_unit = UnitOfTemperature.CELSIUS + _attr_translation_key = "climate" + _attr_name = None + + @property + def min_temp(self) -> float: + """Return the minimum temperature.""" + return self._device.min_temperature + + @property + def max_temp(self) -> float: + """Return the maximum temperature.""" + return self._device.max_temperature + + @property + def preset_modes(self) -> list[str] | None: + """Return the list of available preset modes.""" + return self._device.preset_modes + + @property + def preset_mode(self) -> str | None: + """Return the current preset mode.""" + return self._device.preset_mode + + @property + def hvac_mode(self) -> HVACMode | None: + """Return the current HVAC mode.""" + return SWITCHBOT_CLIMATE_TO_HASS_HVAC_MODE.get( + self._device.hvac_mode, HVACMode.OFF + ) + + @property + def hvac_modes(self) -> list[HVACMode]: + """Return the list of available HVAC modes.""" + return [ + SWITCHBOT_CLIMATE_TO_HASS_HVAC_MODE[mode] + for mode in self._device.hvac_modes + ] + + @property + def hvac_action(self) -> HVACAction | None: + """Return the current HVAC action.""" + return SWITCHBOT_ACTION_TO_HASS_HVAC_ACTION.get( + self._device.hvac_action, HVACAction.OFF + ) + + @property + def current_temperature(self) -> float | None: + """Return the current temperature.""" + return self._device.current_temperature + + @property + def target_temperature(self) -> float | None: + """Return the temperature we try to reach.""" + return self._device.target_temperature + + @property + def target_temperature_step(self) -> float: + """Return the supported step of target temperature.""" + return 0.5 + + @exception_handler + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: + """Set new HVAC mode.""" + return await self._device.set_hvac_mode( + HASS_HVAC_MODE_TO_SWITCHBOT_CLIMATE[hvac_mode] + ) + + @exception_handler + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set new preset mode.""" + return await self._device.set_preset_mode(preset_mode) + + @exception_handler + async def async_set_temperature(self, **kwargs: Any) -> None: + """Set new target temperature.""" + temperature = kwargs.get(ATTR_TEMPERATURE) + return await self._device.set_target_temperature(temperature) diff --git a/homeassistant/components/switchbot/const.py b/homeassistant/components/switchbot/const.py index 4c29ed7d67298f..3925aa4a58928a 100644 --- a/homeassistant/components/switchbot/const.py +++ b/homeassistant/components/switchbot/const.py @@ -58,6 +58,7 @@ class SupportedModels(StrEnum): K11_PLUS_VACUUM = "k11+_vacuum" GARAGE_DOOR_OPENER = "garage_door_opener" CLIMATE_PANEL = "climate_panel" + SMART_THERMOSTAT_RADIATOR = "smart_thermostat_radiator" CONNECTABLE_SUPPORTED_MODEL_TYPES = { @@ -95,6 +96,7 @@ class SupportedModels(StrEnum): SwitchbotModel.K11_VACUUM: SupportedModels.K11_PLUS_VACUUM, SwitchbotModel.GARAGE_DOOR_OPENER: SupportedModels.GARAGE_DOOR_OPENER, SwitchbotModel.CLIMATE_PANEL: SupportedModels.CLIMATE_PANEL, + SwitchbotModel.SMART_THERMOSTAT_RADIATOR: SupportedModels.SMART_THERMOSTAT_RADIATOR, } NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = { @@ -132,6 +134,7 @@ class SupportedModels(StrEnum): SwitchbotModel.PLUG_MINI_EU, SwitchbotModel.RELAY_SWITCH_2PM, SwitchbotModel.GARAGE_DOOR_OPENER, + SwitchbotModel.SMART_THERMOSTAT_RADIATOR, } ENCRYPTED_SWITCHBOT_MODEL_TO_CLASS: dict[ @@ -153,6 +156,7 @@ class SupportedModels(StrEnum): SwitchbotModel.PLUG_MINI_EU: switchbot.SwitchbotRelaySwitch, SwitchbotModel.RELAY_SWITCH_2PM: switchbot.SwitchbotRelaySwitch2PM, SwitchbotModel.GARAGE_DOOR_OPENER: switchbot.SwitchbotRelaySwitch, + SwitchbotModel.SMART_THERMOSTAT_RADIATOR: switchbot.SwitchbotSmartThermostatRadiator, } HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL = { diff --git a/homeassistant/components/switchbot/icons.json b/homeassistant/components/switchbot/icons.json index db0ccd3f49c415..8e0c3d21756a1e 100644 --- a/homeassistant/components/switchbot/icons.json +++ b/homeassistant/components/switchbot/icons.json @@ -127,6 +127,22 @@ "medium": "mdi:water" } } + }, + "climate": { + "climate": { + "state_attributes": { + "preset_mode": { + "state": { + "schedule": "mdi:calendar-clock", + "manual": "mdi:hand-back-right", + "off": "mdi:hvac-off", + "economic": "mdi:leaf", + "comfort": "mdi:sofa-outline", + "fast_heating": "mdi:thermometer-chevron-up" + } + } + } + } } } } diff --git a/homeassistant/components/switchbot/strings.json b/homeassistant/components/switchbot/strings.json index 91b3ba7b85f000..ae3b09c6b0ce10 100644 --- a/homeassistant/components/switchbot/strings.json +++ b/homeassistant/components/switchbot/strings.json @@ -302,6 +302,22 @@ } } } + }, + "climate": { + "climate": { + "state_attributes": { + "preset_mode": { + "state": { + "schedule": "Schedule", + "manual": "[%key:common::state::manual%]", + "off": "[%key:common::state::off%]", + "economic": "Economic", + "comfort": "Comfort", + "fast_heating": "Fast Heating" + } + } + } + } } }, "exceptions": { diff --git a/homeassistant/components/switchbot/water_heater.py b/homeassistant/components/switchbot/water_heater.py deleted file mode 100644 index a66b41db2bdc59..00000000000000 --- a/homeassistant/components/switchbot/water_heater.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Support for switchbot water heaters.""" - -import logging - -import switchbot - -from homeassistant.components.water_heater import ( - WaterHeaterEntity, - WaterHeaterEntityFeature, -) -from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback - -from .coordinator import SwitchbotConfigEntry -from .entity import SwitchbotEntity - -PARALLEL_UPDATES = 0 -_LOGGER = logging.getLogger(__name__) - - -async def async_setup_entry( - hass: HomeAssistant, - entry: SwitchbotConfigEntry, - async_add_entities: AddConfigEntryEntitiesCallback, -) -> None: - """Set up Switchbot based on a config entry.""" - coordinator = entry.runtime_data - async_add_entities([SwitchbotThermostat(coordinator)]) - - -class SwitchbotThermostat(SwitchbotEntity, WaterHeaterEntity): - """Representation of a Switchbot Thermostat.""" - - _attr_supported_features = WaterHeaterEntityFeature.TARGET_TEMPERATURE - _device: switchbot.SwitchbotThermostat - _attr_translation_key = "thermostat" - _attr_name = None - - @property - def target_temperature(self) -> float | None: - """Return the temperature we try to reach.""" - return self._device.target_temperature - - @property - def current_temperature(self) -> float | None: - """Return the current temperature.""" - return self._device.current_temperature - - @property - def min_temp(self) -> float: - """Return the minimum temperature.""" - return self._device.min_temp - - @property - def max_temp(self) -> float: - """Return the maximum temperature.""" - return self._device.max_temp diff --git a/tests/components/switchbot/__init__.py b/tests/components/switchbot/__init__.py index 4a9fcd772b20c7..86de074202c6b5 100644 --- a/tests/components/switchbot/__init__.py +++ b/tests/components/switchbot/__init__.py @@ -1200,3 +1200,26 @@ def make_advertisement( connectable=True, tx_power=-127, ) + + +SMART_THERMOSTAT_RADIATOR_SERVICE_INFO = BluetoothServiceInfoBleak( + name="Smart Thermostat Radiator", + manufacturer_data={2409: b"\xb0\xe9\xfe\xa2T|6\xe4\x00\x9c\xa3A\x00"}, + service_data={ + "0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00 d\x00\x116@", + }, + service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], + address="AA:BB:CC:DD:EE:FF", + rssi=-60, + source="local", + advertisement=generate_advertisement_data( + local_name="Smart Thermostat Radiator", + manufacturer_data={2409: b"\xb0\xe9\xfe\xa2T|6\xe4\x00\x9c\xa3A\x00"}, + service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"\x00 d\x00\x116@"}, + service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"], + ), + device=generate_ble_device("AA:BB:CC:DD:EE:FF", "Smart Thermostat Radiator"), + time=0, + connectable=True, + tx_power=-127, +) diff --git a/tests/components/switchbot/test_climate.py b/tests/components/switchbot/test_climate.py new file mode 100644 index 00000000000000..585c72e7bbdb4a --- /dev/null +++ b/tests/components/switchbot/test_climate.py @@ -0,0 +1,118 @@ +"""Tests for the Switchbot climate integration.""" + +from collections.abc import Callable +from unittest.mock import AsyncMock, patch + +import pytest +from switchbot import SwitchbotOperationError + +from homeassistant.components.climate import ( + DOMAIN as CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + SERVICE_SET_PRESET_MODE, + SERVICE_SET_TEMPERATURE, + HVACMode, +) +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError + +from . import SMART_THERMOSTAT_RADIATOR_SERVICE_INFO + +from tests.common import MockConfigEntry +from tests.components.bluetooth import inject_bluetooth_service_info + + +@pytest.mark.parametrize( + ("service", "service_data", "mock_method"), + [ + (SERVICE_SET_HVAC_MODE, {"hvac_mode": HVACMode.HEAT}, "set_hvac_mode"), + (SERVICE_SET_PRESET_MODE, {"preset_mode": "manual"}, "set_preset_mode"), + (SERVICE_SET_TEMPERATURE, {"temperature": 22}, "set_target_temperature"), + ], +) +async def test_smart_thermostat_radiator_controlling( + hass: HomeAssistant, + mock_entry_encrypted_factory: Callable[[str], MockConfigEntry], + service: str, + service_data: dict, + mock_method: str, +) -> None: + """Test controlling the smart thermostat radiator with different services.""" + inject_bluetooth_service_info(hass, SMART_THERMOSTAT_RADIATOR_SERVICE_INFO) + + entry = mock_entry_encrypted_factory("smart_thermostat_radiator") + entity_id = "climate.test_name" + entry.add_to_hass(hass) + + mocked_instance = AsyncMock(return_value=True) + mocked_none_instance = AsyncMock(return_value=None) + with patch.multiple( + "homeassistant.components.switchbot.climate.switchbot.SwitchbotSmartThermostatRadiator", + get_basic_info=mocked_none_instance, + update=mocked_none_instance, + **{mock_method: mocked_instance}, + ): + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + await hass.services.async_call( + CLIMATE_DOMAIN, + service, + {**service_data, ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + mocked_instance.assert_awaited_once() + + +@pytest.mark.parametrize( + ("service", "service_data", "mock_method"), + [ + (SERVICE_SET_HVAC_MODE, {"hvac_mode": HVACMode.HEAT}, "set_hvac_mode"), + (SERVICE_SET_PRESET_MODE, {"preset_mode": "manual"}, "set_preset_mode"), + (SERVICE_SET_TEMPERATURE, {"temperature": 22}, "set_target_temperature"), + ], +) +@pytest.mark.parametrize( + ("exception", "error_message"), + [ + ( + SwitchbotOperationError("Operation failed"), + "An error occurred while performing the action: Operation failed", + ), + ], +) +async def test_exception_handling_smart_thermostat_radiator_service( + hass: HomeAssistant, + mock_entry_encrypted_factory: Callable[[str], MockConfigEntry], + service: str, + service_data: dict, + mock_method: str, + exception: Exception, + error_message: str, +) -> None: + """Test exception handling for smart thermostat radiator service with exception.""" + inject_bluetooth_service_info(hass, SMART_THERMOSTAT_RADIATOR_SERVICE_INFO) + + entry = mock_entry_encrypted_factory("smart_thermostat_radiator") + entry.add_to_hass(hass) + entity_id = "climate.test_name" + + mocked_none_instance = AsyncMock(return_value=None) + with patch.multiple( + "homeassistant.components.switchbot.climate.switchbot.SwitchbotSmartThermostatRadiator", + get_basic_info=mocked_none_instance, + update=mocked_none_instance, + **{mock_method: AsyncMock(side_effect=exception)}, + ): + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + with pytest.raises(HomeAssistantError, match=error_message): + await hass.services.async_call( + CLIMATE_DOMAIN, + service, + {**service_data, ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) From 3f604e62963c758646b6f6dd27728116dce89e78 Mon Sep 17 00:00:00 2001 From: zerzhang <916250497@qq.com> Date: Wed, 29 Oct 2025 20:22:11 +0800 Subject: [PATCH 3/6] fix --- homeassistant/components/switchbot/icons.json | 5 +---- homeassistant/components/switchbot/strings.json | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/switchbot/icons.json b/homeassistant/components/switchbot/icons.json index 8e0c3d21756a1e..5ae5c8983ec19b 100644 --- a/homeassistant/components/switchbot/icons.json +++ b/homeassistant/components/switchbot/icons.json @@ -135,10 +135,7 @@ "state": { "schedule": "mdi:calendar-clock", "manual": "mdi:hand-back-right", - "off": "mdi:hvac-off", - "economic": "mdi:leaf", - "comfort": "mdi:sofa-outline", - "fast_heating": "mdi:thermometer-chevron-up" + "off": "mdi:hvac-off" } } } diff --git a/homeassistant/components/switchbot/strings.json b/homeassistant/components/switchbot/strings.json index ae3b09c6b0ce10..2796dc1adab275 100644 --- a/homeassistant/components/switchbot/strings.json +++ b/homeassistant/components/switchbot/strings.json @@ -310,10 +310,7 @@ "state": { "schedule": "Schedule", "manual": "[%key:common::state::manual%]", - "off": "[%key:common::state::off%]", - "economic": "Economic", - "comfort": "Comfort", - "fast_heating": "Fast Heating" + "off": "[%key:common::state::off%]" } } } From a4c8ad54189bd2fb6479b25f29e353a48471c0bf Mon Sep 17 00:00:00 2001 From: zerzhang <916250497@qq.com> Date: Tue, 11 Nov 2025 19:14:50 +0800 Subject: [PATCH 4/6] fix --- homeassistant/components/switchbot/climate.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/homeassistant/components/switchbot/climate.py b/homeassistant/components/switchbot/climate.py index d5d1db27d1d8c1..79b05388d2264f 100644 --- a/homeassistant/components/switchbot/climate.py +++ b/homeassistant/components/switchbot/climate.py @@ -64,6 +64,7 @@ class SwitchBotClimateEntity(SwitchbotEntity, ClimateEntity): | ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON ) + _attr_target_temperature_step = 0.5 _attr_temperature_unit = UnitOfTemperature.CELSIUS _attr_translation_key = "climate" _attr_name = None @@ -120,11 +121,6 @@ def target_temperature(self) -> float | None: """Return the temperature we try to reach.""" return self._device.target_temperature - @property - def target_temperature_step(self) -> float: - """Return the supported step of target temperature.""" - return 0.5 - @exception_handler async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: """Set new HVAC mode.""" From 6ee9d87dd05b94679c01b13a197ba77ed73a11ca Mon Sep 17 00:00:00 2001 From: zerzhang <916250497@qq.com> Date: Tue, 11 Nov 2025 19:47:33 +0800 Subject: [PATCH 5/6] fix --- homeassistant/components/switchbot/icons.json | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/switchbot/icons.json b/homeassistant/components/switchbot/icons.json index 5ae5c8983ec19b..856b4bc1f983b9 100644 --- a/homeassistant/components/switchbot/icons.json +++ b/homeassistant/components/switchbot/icons.json @@ -1,5 +1,18 @@ { "entity": { + "climate": { + "climate": { + "state_attributes": { + "preset_mode": { + "state": { + "manual": "mdi:hand-back-right", + "off": "mdi:hvac-off", + "schedule": "mdi:calendar-clock" + } + } + } + } + }, "fan": { "air_purifier": { "default": "mdi:air-purifier", @@ -127,19 +140,6 @@ "medium": "mdi:water" } } - }, - "climate": { - "climate": { - "state_attributes": { - "preset_mode": { - "state": { - "schedule": "mdi:calendar-clock", - "manual": "mdi:hand-back-right", - "off": "mdi:hvac-off" - } - } - } - } } } } From 7d41cbd4f4849bba462754a249e217512aa0d39f Mon Sep 17 00:00:00 2001 From: zerzhang <916250497@qq.com> Date: Tue, 11 Nov 2025 19:52:42 +0800 Subject: [PATCH 6/6] fix --- .../components/switchbot/strings.json | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/switchbot/strings.json b/homeassistant/components/switchbot/strings.json index 2796dc1adab275..06b8732f9ccd3d 100644 --- a/homeassistant/components/switchbot/strings.json +++ b/homeassistant/components/switchbot/strings.json @@ -100,6 +100,19 @@ "name": "Unlocked alarm" } }, + "climate": { + "climate": { + "state_attributes": { + "preset_mode": { + "state": { + "manual": "[%key:common::state::manual%]", + "off": "[%key:common::state::off%]", + "schedule": "Schedule" + } + } + } + } + }, "cover": { "cover": { "state_attributes": { @@ -302,19 +315,6 @@ } } } - }, - "climate": { - "climate": { - "state_attributes": { - "preset_mode": { - "state": { - "schedule": "Schedule", - "manual": "[%key:common::state::manual%]", - "off": "[%key:common::state::off%]" - } - } - } - } } }, "exceptions": {