diff --git a/custom_components/adaptive_lighting/const.py b/custom_components/adaptive_lighting/const.py index 64620927..c0cb2eaf 100644 --- a/custom_components/adaptive_lighting/const.py +++ b/custom_components/adaptive_lighting/const.py @@ -1,7 +1,12 @@ """Constants for the Adaptive Lighting integration.""" from homeassistant.components.light import VALID_TRANSITION -from homeassistant.const import CONF_ENTITY_ID +from homeassistant.const import ( + CONF_ENTITY_ID, + SERVICE_TOGGLE, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, +) from homeassistant.helpers import selector import homeassistant.helpers.config_validation as cv import voluptuous as vol @@ -201,6 +206,19 @@ DOCS[CONF_USE_DEFAULTS] = ( 'documented defaults), or "configuration" (reverts to switch config defaults). ⚙️' ) +CONF_WHICH_SWITCH, DEFAULT_WHICH_SWITCH = "switch_type", "main" +DOCS[CONF_WHICH_SWITCH] = ( + "Which switch to target in this service call. Options: " + '"main" (default, targets the main switch), "sleep", "brightness", "color"' +) +DOCS[ + SERVICE_TURN_ON +] = "Turn on an Adaptive Lighting main/sleep/brightness/color switch" +DOCS[ + SERVICE_TURN_OFF +] = "Turn off an Adaptive Lighting main/sleep/brightness/color switch" +DOCS[SERVICE_TOGGLE] = "Toggle an Adaptive Lighting main/sleep/brightness/color switch" + TURNING_OFF_DELAY = 5 DOCS_MANUAL_CONTROL = { @@ -339,6 +357,14 @@ def apply_service_schema(initial_transition: int = 1): ) +SERVICE_TOGGLE_SCHEMA = vol.Schema( + { + vol.Optional(CONF_ENTITY_ID): cv.entity_ids, + vol.Optional(CONF_LIGHTS, default=[]): cv.entity_ids, + vol.Optional(CONF_WHICH_SWITCH): cv.string, + } +) + SET_MANUAL_CONTROL_SCHEMA = vol.Schema( { vol.Optional(CONF_ENTITY_ID): cv.entity_ids, diff --git a/custom_components/adaptive_lighting/switch.py b/custom_components/adaptive_lighting/switch.py index 5b816b67..b28a2abc 100644 --- a/custom_components/adaptive_lighting/switch.py +++ b/custom_components/adaptive_lighting/switch.py @@ -56,6 +56,7 @@ from homeassistant.const import ( EVENT_CALL_SERVICE, EVENT_HOMEASSISTANT_STARTED, EVENT_STATE_CHANGED, + SERVICE_TOGGLE, SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, @@ -129,6 +130,7 @@ from .const import ( CONF_TRANSITION, CONF_TURN_ON_LIGHTS, CONF_USE_DEFAULTS, + CONF_WHICH_SWITCH, DOMAIN, EXTRA_VALIDATION, ICON_BRIGHTNESS, @@ -138,6 +140,7 @@ from .const import ( SERVICE_APPLY, SERVICE_CHANGE_SWITCH_SETTINGS, SERVICE_SET_MANUAL_CONTROL, + SERVICE_TOGGLE_SCHEMA, SET_MANUAL_CONTROL_SCHEMA, SLEEP_MODE_SWITCH, SUN_EVENT_MIDNIGHT, @@ -472,6 +475,69 @@ async def async_setup_entry( update_before_add=True, ) + @callback + async def handle_turn_on(service_call: ServiceCall): + """Toggles the specified switch.""" + data = service_call.data + _LOGGER.debug( + "Called 'adaptive_lighting.turn_on' service with '%s'", + data, + ) + switches = _get_switches_from_service_call(hass, service_call) + these_switches = [] # CONF_WHICH_SWITCH == "main" is default + if data[CONF_WHICH_SWITCH] == "sleep": + these_switches = [s.sleep_mode_switch for s in switches] + elif data[CONF_WHICH_SWITCH] == "brightness": + these_switches = [s.adapt_brightness_switch for s in switches] + elif data[CONF_WHICH_SWITCH] == "color": + these_switches = [s.adapt_color_switch for s in switches] + _LOGGER.debug("Turning on switches [%s]", these_switches) + for switch in these_switches: + await switch.async_turn_on() + + @callback + async def handle_turn_off(service_call: ServiceCall): + """Toggles the specified switch.""" + data = service_call.data + _LOGGER.debug( + "Called 'adaptive_lighting.turn_off' service with '%s'", + data, + ) + switches = _get_switches_from_service_call(hass, service_call) + these_switches = [] # CONF_WHICH_SWITCH == "main" is default + if data[CONF_WHICH_SWITCH] == "sleep": + these_switches = [s.sleep_mode_switch for s in switches] + elif data[CONF_WHICH_SWITCH] == "brightness": + these_switches = [s.adapt_brightness_switch for s in switches] + elif data[CONF_WHICH_SWITCH] == "color": + these_switches = [s.adapt_color_switch for s in switches] + _LOGGER.debug("Turning off switches [%s]", these_switches) + for switch in these_switches: + await switch.async_turn_off() + + @callback + async def handle_toggle(service_call: ServiceCall): + """Toggles the specified switch.""" + data = service_call.data + _LOGGER.debug( + "Called 'adaptive_lighting.toggle' service with '%s'", + data, + ) + switches = _get_switches_from_service_call(hass, service_call) + these_switches = [] # CONF_WHICH_SWITCH == "main" is default + if data[CONF_WHICH_SWITCH] == "sleep": + these_switches = [s.sleep_mode_switch for s in switches] + elif data[CONF_WHICH_SWITCH] == "brightness": + these_switches = [s.adapt_brightness_switch for s in switches] + elif data[CONF_WHICH_SWITCH] == "color": + these_switches = [s.adapt_color_switch for s in switches] + _LOGGER.debug("Toggling switches [%s]", these_switches) + for switch in these_switches: + if switch.is_on: + await switch.async_turn_off() + else: + await switch.async_turn_on() + @callback async def handle_apply(service_call: ServiceCall): """Handle the entity service apply.""" @@ -544,6 +610,30 @@ async def async_setup_entry( ), # pylint: disable=protected-access ) + # Register `turn_on` service + hass.services.async_register( + domain=DOMAIN, + service=SERVICE_TURN_ON, + service_func=handle_turn_on, + schema=SERVICE_TOGGLE_SCHEMA, + ) + + # Register `turn_off` service + hass.services.async_register( + domain=DOMAIN, + service=SERVICE_TURN_OFF, + service_func=handle_turn_off, + schema=SERVICE_TOGGLE_SCHEMA, + ) + + # Register `toggle` service + hass.services.async_register( + domain=DOMAIN, + service=SERVICE_TOGGLE, + service_func=handle_toggle, + schema=SERVICE_TOGGLE_SCHEMA, + ) + # Register `set_manual_control` service hass.services.async_register( domain=DOMAIN, diff --git a/tests/test_switch.py b/tests/test_switch.py index f6d671f4..3b8f615b 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -25,6 +25,7 @@ from homeassistant.components.adaptive_lighting.const import ( CONF_TRANSITION, CONF_TURN_ON_LIGHTS, CONF_USE_DEFAULTS, + CONF_WHICH_SWITCH, DEFAULT_MAX_BRIGHTNESS, DEFAULT_NAME, DEFAULT_SLEEP_BRIGHTNESS, @@ -64,6 +65,8 @@ from homeassistant.const import ( CONF_NAME, CONF_PLATFORM, EVENT_STATE_CHANGED, + SERVICE_TOGGLE, + SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_OFF, STATE_ON, @@ -1246,6 +1249,82 @@ async def test_area(hass): assert light.entity_id not in switch.turn_on_off_listener.last_service_data +@pytest.mark.dependency(depends=GLOBAL_TEST_DEPENDENCIES) +async def test_switch_turn_on_off_toggle(hass): + """Test adaptive_lighting.change_switch_settings service.""" + switch, (_, _, light) = await setup_lights_and_switch(hass) + entity_id = switch.entity_id + assert entity_id not in switch._lights + + async def turn_on(which: str, **kwargs): + await hass.services.async_call( + DOMAIN, + SERVICE_TURN_ON, + { + ATTR_ENTITY_ID: entity_id, + CONF_WHICH_SWITCH: which, + **kwargs, + }, + blocking=True, + ) + await hass.async_block_till_done() + + async def turn_off(which: str, **kwargs): + await hass.services.async_call( + DOMAIN, + SERVICE_TURN_OFF, + { + ATTR_ENTITY_ID: entity_id, + CONF_WHICH_SWITCH: which, + **kwargs, + }, + blocking=True, + ) + await hass.async_block_till_done() + + async def toggle(which: str, **kwargs): + await hass.services.async_call( + DOMAIN, + SERVICE_TOGGLE, + { + ATTR_ENTITY_ID: entity_id, + CONF_WHICH_SWITCH: which, + **kwargs, + }, + blocking=True, + ) + await hass.async_block_till_done() + + # Test sleep + await turn_on("sleep") + assert switch.sleep_mode_switch.is_on + await turn_off("sleep") + assert not switch.sleep_mode_switch.is_on + await toggle("sleep") + assert switch.sleep_mode_switch.is_on + # Test brightness + await turn_on("brightness") + assert switch.adapt_brightness_switch.is_on + await turn_off("brightness") + assert not switch.adapt_brightness_switch.is_on + await toggle("brightness") + assert switch.adapt_brightness_switch.is_on + # Test color + await turn_on("color") + assert switch.adapt_color_switch.is_on + await turn_off("color") + assert not switch.adapt_color_switch.is_on + await toggle("color") + assert switch.adapt_color_switch.is_on + # Test main + await turn_on("main") + assert switch.is_on + await turn_off("main") + assert not switch.is_on + await toggle("main") + assert switch.is_on + + @pytest.mark.dependency(depends=GLOBAL_TEST_DEPENDENCIES) async def test_change_switch_settings_service(hass): """Test adaptive_lighting.change_switch_settings service."""