diff --git a/homeassistant/components/modern_forms/binary_sensor.py b/homeassistant/components/modern_forms/binary_sensor.py index e9aa1ab19eca..075cb7d4e14f 100644 --- a/homeassistant/components/modern_forms/binary_sensor.py +++ b/homeassistant/components/modern_forms/binary_sensor.py @@ -20,16 +20,19 @@ async def async_setup_entry( """Set up Modern Forms binary sensors.""" coordinator = entry.runtime_data - binary_sensors: list[ModernFormsBinarySensor] = [ - ModernFormsFanSleepTimerActive(entry.entry_id, coordinator), - ] + binary_sensors: list[ModernFormsBinarySensor] = [] - # Only setup light sleep timer sensor if light unit installed - if coordinator.data.info.light_type: + if coordinator.data.has_sleep_timer(): binary_sensors.append( - ModernFormsLightSleepTimerActive(entry.entry_id, coordinator) + ModernFormsFanSleepTimerActive(entry.entry_id, coordinator) ) + # Only setup light sleep timer sensor if light unit installed + if coordinator.data.info.light_type: + binary_sensors.append( + ModernFormsLightSleepTimerActive(entry.entry_id, coordinator) + ) + async_add_entities(binary_sensors) diff --git a/homeassistant/components/modern_forms/fan.py b/homeassistant/components/modern_forms/fan.py index 5fad755792f4..e072bcb1bedf 100644 --- a/homeassistant/components/modern_forms/fan.py +++ b/homeassistant/components/modern_forms/fan.py @@ -7,6 +7,7 @@ import voluptuous as vol from homeassistant.components.fan import FanEntity, FanEntityFeature from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_platform from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.util.percentage import ( @@ -19,6 +20,7 @@ from . import modernforms_exception_handler from .const import ( ATTR_SLEEP_TIME, CLEAR_TIMER, + DOMAIN, OPT_ON, OPT_SPEED, OPT_WIND, @@ -186,6 +188,11 @@ class ModernFormsFanEntity(FanEntity, ModernFormsDeviceEntity): sleep_time: int, ) -> None: """Set a Modern Forms light sleep timer.""" + if not self.coordinator.data.has_sleep_timer(): + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="sleep_timer_not_supported", + ) await self.coordinator.modern_forms.fan(sleep=sleep_time * 60) @modernforms_exception_handler @@ -193,4 +200,9 @@ class ModernFormsFanEntity(FanEntity, ModernFormsDeviceEntity): self, ) -> None: """Clear a Modern Forms fan sleep timer.""" + if not self.coordinator.data.has_sleep_timer(): + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="sleep_timer_not_supported", + ) await self.coordinator.modern_forms.fan(sleep=CLEAR_TIMER) diff --git a/homeassistant/components/modern_forms/light.py b/homeassistant/components/modern_forms/light.py index 04707bd459a3..62ab29edfdc0 100644 --- a/homeassistant/components/modern_forms/light.py +++ b/homeassistant/components/modern_forms/light.py @@ -8,6 +8,7 @@ import voluptuous as vol from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_platform from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from homeassistant.util.percentage import ( @@ -19,6 +20,7 @@ from . import modernforms_exception_handler from .const import ( ATTR_SLEEP_TIME, CLEAR_TIMER, + DOMAIN, OPT_BRIGHTNESS, OPT_ON, SERVICE_CLEAR_LIGHT_SLEEP_TIMER, @@ -159,6 +161,11 @@ class ModernFormsLightEntity(ModernFormsDeviceEntity, LightEntity): sleep_time: int, ) -> None: """Set a Modern Forms light sleep timer.""" + if not self.coordinator.data.has_sleep_timer(): + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="sleep_timer_not_supported", + ) await self._async_control_light(sleep=sleep_time * 60) @modernforms_exception_handler @@ -166,6 +173,11 @@ class ModernFormsLightEntity(ModernFormsDeviceEntity, LightEntity): self, ) -> None: """Clear a Modern Forms light sleep timer.""" + if not self.coordinator.data.has_sleep_timer(): + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="sleep_timer_not_supported", + ) await self._async_control_light(sleep=CLEAR_TIMER) async def _async_control_light(self, **kwargs: Any) -> None: diff --git a/homeassistant/components/modern_forms/sensor.py b/homeassistant/components/modern_forms/sensor.py index 8ba086d4dfe0..ee8b0d61ab8c 100644 --- a/homeassistant/components/modern_forms/sensor.py +++ b/homeassistant/components/modern_forms/sensor.py @@ -22,16 +22,19 @@ async def async_setup_entry( """Set up Modern Forms sensor based on a config entry.""" coordinator = entry.runtime_data - sensors: list[ModernFormsSensor] = [ - ModernFormsFanTimerRemainingTimeSensor(entry.entry_id, coordinator), - ] + sensors: list[ModernFormsSensor] = [] - # Only setup light sleep timer sensor if light unit installed - if coordinator.data.info.light_type: + if coordinator.data.has_sleep_timer(): sensors.append( - ModernFormsLightTimerRemainingTimeSensor(entry.entry_id, coordinator) + ModernFormsFanTimerRemainingTimeSensor(entry.entry_id, coordinator) ) + # Only setup light sleep timer sensor if light unit installed + if coordinator.data.info.light_type: + sensors.append( + ModernFormsLightTimerRemainingTimeSensor(entry.entry_id, coordinator) + ) + async_add_entities(sensors) diff --git a/homeassistant/components/modern_forms/strings.json b/homeassistant/components/modern_forms/strings.json index 0e864abda3f2..2cf7ed00be98 100644 --- a/homeassistant/components/modern_forms/strings.json +++ b/homeassistant/components/modern_forms/strings.json @@ -71,6 +71,9 @@ }, "invalid_response": { "message": "Invalid response from the Modern Forms device" + }, + "sleep_timer_not_supported": { + "message": "This fan does not support sleep timers" } }, "services": { diff --git a/homeassistant/components/modern_forms/switch.py b/homeassistant/components/modern_forms/switch.py index 93a59d83657b..f08a6bdef400 100644 --- a/homeassistant/components/modern_forms/switch.py +++ b/homeassistant/components/modern_forms/switch.py @@ -19,10 +19,13 @@ async def async_setup_entry( """Set up Modern Forms switch based on a config entry.""" coordinator = entry.runtime_data - switches = [ + switches: list[ModernFormsSwitch] = [ ModernFormsAwaySwitch(entry.entry_id, coordinator), - ModernFormsAdaptiveLearningSwitch(entry.entry_id, coordinator), ] + + if coordinator.data.has_adaptive_learning(): + switches.append(ModernFormsAdaptiveLearningSwitch(entry.entry_id, coordinator)) + async_add_entities(switches) diff --git a/tests/components/modern_forms/test_binary_sensor.py b/tests/components/modern_forms/test_binary_sensor.py index a605b86d4844..9717c507b842 100644 --- a/tests/components/modern_forms/test_binary_sensor.py +++ b/tests/components/modern_forms/test_binary_sensor.py @@ -5,7 +5,7 @@ from homeassistant.components.modern_forms.const import DOMAIN from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er -from . import init_integration +from . import init_integration, init_integration_gen4 from tests.test_util.aiohttp import AiohttpClientMocker @@ -43,3 +43,26 @@ async def test_binary_sensors( state = hass.states.get("binary_sensor.modernformsfan_fan_sleep_timer_active") assert state assert state.state == "off" + + +async def test_no_sleep_timer_binary_sensors_on_gen4( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test the sleep-timer binary sensors aren't created for Gen4 fans.""" + await init_integration_gen4(hass, aioclient_mock) + + # Both entities are disabled by default, so checking hass.states here + # wouldn't distinguish "not created" from "created but disabled" -- + # check the entity registry instead. + assert ( + entity_registry.async_get("binary_sensor.modernformsfan_fan_sleep_timer_active") + is None + ) + assert ( + entity_registry.async_get( + "binary_sensor.modernformsfan_light_sleep_timer_active" + ) + is None + ) diff --git a/tests/components/modern_forms/test_fan.py b/tests/components/modern_forms/test_fan.py index f81ce39dba81..8400cc6ad824 100644 --- a/tests/components/modern_forms/test_fan.py +++ b/tests/components/modern_forms/test_fan.py @@ -43,6 +43,7 @@ from homeassistant.helpers import entity_registry as er from . import ( init_integration, + init_integration_gen4, modern_forms_breeze_active_call_mock, modern_forms_breeze_call_mock, ) @@ -395,3 +396,41 @@ async def test_turn_off_does_not_touch_wind( ) await hass.async_block_till_done() fan_mock.assert_called_once_with(on=False) + + +async def test_fan_sleep_timer_not_supported_gen4( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test setting a sleep timer on a Gen4 fan raises an error.""" + await init_integration_gen4(hass, aioclient_mock) + + with pytest.raises(HomeAssistantError) as exc_info: + await hass.services.async_call( + DOMAIN, + SERVICE_SET_FAN_SLEEP_TIMER, + {ATTR_ENTITY_ID: "fan.modernformsfan_fan", ATTR_SLEEP_TIME: 1}, + blocking=True, + ) + + assert exc_info.value.translation_domain == DOMAIN + assert exc_info.value.translation_key == "sleep_timer_not_supported" + + +async def test_clear_fan_sleep_timer_not_supported_gen4( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test clearing a sleep timer on a Gen4 fan raises an error.""" + await init_integration_gen4(hass, aioclient_mock) + + with pytest.raises(HomeAssistantError) as exc_info: + await hass.services.async_call( + DOMAIN, + SERVICE_CLEAR_FAN_SLEEP_TIMER, + {ATTR_ENTITY_ID: "fan.modernformsfan_fan"}, + blocking=True, + ) + + assert exc_info.value.translation_domain == DOMAIN + assert exc_info.value.translation_key == "sleep_timer_not_supported" diff --git a/tests/components/modern_forms/test_light.py b/tests/components/modern_forms/test_light.py index f7e4b5e98013..300db5bde54f 100644 --- a/tests/components/modern_forms/test_light.py +++ b/tests/components/modern_forms/test_light.py @@ -326,28 +326,39 @@ async def test_light_change_state_gen4( light_mock.assert_not_called() -async def test_sleep_timer_services_gen4( - hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +async def test_light_sleep_timer_not_supported_gen4( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, ) -> None: - """Test Gen4 sleep timer services pass sleep through light_fixture().""" + """Test setting a sleep timer on a Gen4 light fixture raises an error.""" await init_integration_gen4(hass, aioclient_mock) - with patch("aiomodernforms.ModernFormsDevice.light_fixture") as light_fixture_mock: + with pytest.raises(HomeAssistantError) as exc_info: await hass.services.async_call( DOMAIN, SERVICE_SET_LIGHT_SLEEP_TIMER, {ATTR_ENTITY_ID: "light.modernformsfan_uplight", ATTR_SLEEP_TIME: 1}, blocking=True, ) - await hass.async_block_till_done() - light_fixture_mock.assert_called_once_with(2, sleep=60) - with patch("aiomodernforms.ModernFormsDevice.light_fixture") as light_fixture_mock: + assert exc_info.value.translation_domain == DOMAIN + assert exc_info.value.translation_key == "sleep_timer_not_supported" + + +async def test_clear_light_sleep_timer_not_supported_gen4( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test clearing a sleep timer on a Gen4 light fixture raises an error.""" + await init_integration_gen4(hass, aioclient_mock) + + with pytest.raises(HomeAssistantError) as exc_info: await hass.services.async_call( DOMAIN, SERVICE_CLEAR_LIGHT_SLEEP_TIMER, {ATTR_ENTITY_ID: "light.modernformsfan_uplight"}, blocking=True, ) - await hass.async_block_till_done() - light_fixture_mock.assert_called_once_with(2, sleep=0) + + assert exc_info.value.translation_domain == DOMAIN + assert exc_info.value.translation_key == "sleep_timer_not_supported" diff --git a/tests/components/modern_forms/test_sensor.py b/tests/components/modern_forms/test_sensor.py index 9058808443ef..65f5370dc949 100644 --- a/tests/components/modern_forms/test_sensor.py +++ b/tests/components/modern_forms/test_sensor.py @@ -6,7 +6,7 @@ from homeassistant.components.sensor import SensorDeviceClass from homeassistant.const import ATTR_DEVICE_CLASS from homeassistant.core import HomeAssistant -from . import init_integration, modern_forms_timers_set_mock +from . import init_integration, init_integration_gen4, modern_forms_timers_set_mock from tests.test_util.aiohttp import AiohttpClientMocker @@ -51,3 +51,14 @@ async def test_active_sensors( assert state assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.TIMESTAMP datetime.fromisoformat(state.state) + + +async def test_no_sleep_timer_sensors_on_gen4( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test the sleep-timer sensors aren't created for Gen4 fans.""" + await init_integration_gen4(hass, aioclient_mock) + + assert hass.states.get("sensor.modernformsfan_fan_sleep_time") is None + assert hass.states.get("sensor.modernformsfan_light_sleep_time") is None diff --git a/tests/components/modern_forms/test_switch.py b/tests/components/modern_forms/test_switch.py index 3ba6305dfcff..cd80778cd6cf 100644 --- a/tests/components/modern_forms/test_switch.py +++ b/tests/components/modern_forms/test_switch.py @@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er -from . import init_integration +from . import init_integration, init_integration_gen4 from tests.test_util.aiohttp import AiohttpClientMocker @@ -156,3 +156,14 @@ async def test_switch_connection_error( state = hass.states.get("switch.modernformsfan_away_mode") assert state.state == STATE_UNAVAILABLE + + +async def test_no_adaptive_learning_switch_on_gen4( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test the adaptive learning switch isn't created for Gen4 fans.""" + await init_integration_gen4(hass, aioclient_mock) + + assert hass.states.get("switch.modernformsfan_adaptive_learning") is None + assert hass.states.get("switch.modernformsfan_away_mode") is not None