Discover switch entities from Hue behavior_script instances (#101262)

This commit is contained in:
Marcel van der Veldt 2023-10-02 17:15:41 +02:00 committed by GitHub
parent 054407722d
commit 35616e100d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 37 deletions

View File

@ -104,6 +104,14 @@
"unidirectional_incoming": "Unidirectional incoming"
}
}
},
"switch": {
"automation": {
"state": {
"on": "[%key:common::state::enabled%",
"off": "[%key:common::state::disabled%"
}
}
}
},
"options": {

View File

@ -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}"

View File

@ -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": {

View File

@ -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