diff --git a/homeassistant/components/hue/strings.json b/homeassistant/components/hue/strings.json index 1224abb240e..a095c290b12 100644 --- a/homeassistant/components/hue/strings.json +++ b/homeassistant/components/hue/strings.json @@ -104,6 +104,14 @@ "unidirectional_incoming": "Unidirectional incoming" } } + }, + "switch": { + "automation": { + "state": { + "on": "[%key:common::state::enabled%", + "off": "[%key:common::state::disabled%" + } + } } }, "options": { diff --git a/homeassistant/components/hue/switch.py b/homeassistant/components/hue/switch.py index 31b5de3a9a1..0fb2ebd6b52 100644 --- a/homeassistant/components/hue/switch.py +++ b/homeassistant/components/hue/switch.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import Any, TypeAlias from aiohue.v2 import HueBridgeV2 +from aiohue.v2.controllers.config import BehaviorInstance, BehaviorInstanceController from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.sensors import ( LightLevel, @@ -12,7 +13,11 @@ from aiohue.v2.controllers.sensors import ( MotionController, ) -from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity +from homeassistant.components.switch import ( + SwitchDeviceClass, + SwitchEntity, + SwitchEntityDescription, +) from homeassistant.config_entries import ConfigEntry from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant, callback @@ -22,7 +27,9 @@ from .bridge import HueBridge from .const import DOMAIN from .v2.entity import HueBaseEntity -ControllerType: TypeAlias = LightLevelController | MotionController +ControllerType: TypeAlias = ( + BehaviorInstanceController | LightLevelController | MotionController +) SensingService: TypeAlias = LightLevel | Motion @@ -43,11 +50,18 @@ async def async_setup_entry( @callback def register_items(controller: ControllerType): @callback - def async_add_entity(event_type: EventType, resource: SensingService) -> None: + def async_add_entity( + event_type: EventType, resource: SensingService | BehaviorInstance + ) -> None: """Add entity from Hue resource.""" - async_add_entities( - [HueSensingServiceEnabledEntity(bridge, controller, resource)] - ) + if isinstance(resource, BehaviorInstance): + async_add_entities( + [HueBehaviorInstanceEnabledEntity(bridge, controller, resource)] + ) + else: + async_add_entities( + [HueSensingServiceEnabledEntity(bridge, controller, resource)] + ) # add all current items in controller for item in controller: @@ -63,24 +77,13 @@ async def async_setup_entry( # setup for each switch-type hue resource register_items(api.sensors.motion) register_items(api.sensors.light_level) + register_items(api.config.behavior_instance) -class HueSensingServiceEnabledEntity(HueBaseEntity, SwitchEntity): - """Representation of a Switch entity from Hue SensingService.""" +class HueResourceEnabledEntity(HueBaseEntity, SwitchEntity): + """Representation of a Switch entity from a Hue resource that can be toggled enabled.""" - _attr_entity_category = EntityCategory.CONFIG - _attr_device_class = SwitchDeviceClass.SWITCH - - def __init__( - self, - bridge: HueBridge, - controller: LightLevelController | MotionController, - resource: SensingService, - ) -> None: - """Initialize the entity.""" - super().__init__(bridge, controller, resource) - self.resource = resource - self.controller = controller + controller: BehaviorInstanceController | LightLevelController | MotionController @property def is_on(self) -> bool: @@ -98,3 +101,32 @@ class HueSensingServiceEnabledEntity(HueBaseEntity, SwitchEntity): await self.bridge.async_request_call( self.controller.set_enabled, self.resource.id, enabled=False ) + + +class HueSensingServiceEnabledEntity(HueResourceEnabledEntity): + """Representation of a Switch entity from Hue SensingService.""" + + entity_description = SwitchEntityDescription( + key="behavior_instance", + device_class=SwitchDeviceClass.SWITCH, + entity_category=EntityCategory.CONFIG, + ) + + +class HueBehaviorInstanceEnabledEntity(HueResourceEnabledEntity): + """Representation of a Switch entity to enable/disable a Hue Behavior Instance.""" + + resource: BehaviorInstance + + entity_description = SwitchEntityDescription( + key="behavior_instance", + device_class=SwitchDeviceClass.SWITCH, + entity_category=EntityCategory.CONFIG, + has_entity_name=False, + translation_key="automation", + ) + + @property + def name(self) -> str: + """Return name for this entity.""" + return f"Automation: {self.resource.metadata.name}" diff --git a/tests/components/hue/fixtures/v2_resources.json b/tests/components/hue/fixtures/v2_resources.json index 24f433f539c..662e1107ca9 100644 --- a/tests/components/hue/fixtures/v2_resources.json +++ b/tests/components/hue/fixtures/v2_resources.json @@ -2050,37 +2050,53 @@ "type": "temperature" }, { + "id": "9ad57767-e622-4f91-9086-2e5573bc781b", + "type": "behavior_instance", + "script_id": "e73bc72d-96b1-46f8-aa57-729861f80c78", + "enabled": true, + "state": { + "timer_state": "stopped" + }, "configuration": { - "end_state": "last_state", + "duration": { + "seconds": 60 + }, + "what": [ + { + "group": { + "rid": "5e799732-e82e-46ab-b5d9-52b701bd7cbc", + "rtype": "room" + }, + "recall": { + "rid": "732ff1d9-76a7-4630-aad0-c8acc499bb0b", + "rtype": "recipe" + } + } + ], "where": [ { "group": { - "rid": "c14cf1cf-6c7a-4984-b8bb-c5b71aeb70fc", - "rtype": "entertainment_configuration" + "rid": "5e799732-e82e-46ab-b5d9-52b701bd7cbc", + "rtype": "room" } } ] }, "dependees": [ { - "level": "critical", "target": { - "rid": "c14cf1cf-6c7a-4984-b8bb-c5b71aeb70fc", - "rtype": "entertainment_configuration" + "rid": "5e799732-e82e-46ab-b5d9-52b701bd7cbc", + "rtype": "room" }, + "level": "critical", "type": "ResourceDependee" } ], - "enabled": true, - "id": "0670cfb1-2bd7-4237-a0e3-1827a44d7231", + "status": "running", "last_error": "", "metadata": { - "name": "state_after_streaming" - }, - "migrated_from": "/resourcelinks/47450", - "script_id": "7719b841-6b3d-448d-a0e7-601ae9edb6a2", - "status": "running", - "type": "behavior_instance" + "name": "Timer Test" + } }, { "configuration_schema": { diff --git a/tests/components/hue/test_switch.py b/tests/components/hue/test_switch.py index a576b88a7c3..e8cad2bc802 100644 --- a/tests/components/hue/test_switch.py +++ b/tests/components/hue/test_switch.py @@ -14,8 +14,8 @@ async def test_switch( await setup_platform(hass, mock_bridge_v2, "switch") # there shouldn't have been any requests at this point assert len(mock_bridge_v2.mock_requests) == 0 - # 3 entities should be created from test data - assert len(hass.states.async_all()) == 3 + # 4 entities should be created from test data + assert len(hass.states.async_all()) == 4 # test config switch to enable/disable motion sensor test_entity = hass.states.get("switch.hue_motion_sensor_motion") @@ -24,6 +24,13 @@ async def test_switch( assert test_entity.state == "on" assert test_entity.attributes["device_class"] == "switch" + # test config switch to enable/disable a behavior_instance resource (=builtin automation) + test_entity = hass.states.get("switch.automation_timer_test") + assert test_entity is not None + assert test_entity.name == "Automation: Timer Test" + assert test_entity.state == "on" + assert test_entity.attributes["device_class"] == "switch" + async def test_switch_turn_on_service( hass: HomeAssistant, mock_bridge_v2, v2_resources_test_data