1
0
mirror of https://github.com/home-assistant/core.git synced 2026-08-06 05:14:06 +01:00

Fix Hue Mimic Presence automations switch (#177691)

This commit is contained in:
Marcel van der Veldt
2026-08-04 15:43:51 +02:00
committed by GitHub
parent ab2c62ad06
commit eb2d2e7ebe
3 changed files with 134 additions and 1 deletions
+31 -1
View File
@@ -12,6 +12,7 @@ from aiohue.v2.controllers.sensors import (
Motion,
MotionController,
)
from aiohue.v2.models.behavior_instance import PresenceMimickingState
from aiohue.v2.models.behavior_script import BehaviorScriptCategory
from homeassistant.components.switch import (
@@ -143,8 +144,13 @@ class HueResourceEnabledEntity(HueBaseEntity, SwitchEntity):
class HueBehaviorInstanceEnabledEntity(HueResourceEnabledEntity):
"""Representation of a Switch entity to enable/disable a Hue Behavior Instance."""
"""Representation of a Switch entity to enable/disable a Hue Behavior Instance.
Automations that the Hue app runs with a play button, such as mimic
presence, are started and stopped. All others are enabled and disabled.
"""
controller: BehaviorInstanceController
resource: BehaviorInstance
entity_description = SwitchEntityDescription(
@@ -159,6 +165,30 @@ class HueBehaviorInstanceEnabledEntity(HueResourceEnabledEntity):
"""Return name for this entity."""
return f"Automation: {self.resource.metadata.name}"
@property
@override
def is_on(self) -> bool:
"""Return true if the switch is on."""
if (run_state := self.resource.presence_mimicking_state) is not None:
return run_state is PresenceMimickingState.STARTED
return self.resource.enabled
@override
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the entity on."""
if self.resource.presence_mimicking_state is None:
await super().async_turn_on(**kwargs)
return
await self.bridge.async_request_call(self.controller.start, self.resource.id)
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the entity off."""
if self.resource.presence_mimicking_state is None:
await super().async_turn_off(**kwargs)
return
await self.bridge.async_request_call(self.controller.stop, self.resource.id)
class HueMotionSensorEnabledEntity(HueResourceEnabledEntity):
"""Representation of a Switch entity to enable/disable a Hue motion sensor."""
+25
View File
@@ -162,3 +162,28 @@ FAKE_BEHAVIOR_INSTANCE = {
"status": "running",
"type": "behavior_instance",
}
FAKE_PRESENCE_MIMICKING_SCRIPT = {
"configuration_schema": {},
"description": "PM Automation",
"id": "fake_behavior_script_id_2",
"metadata": {"category": "automation", "name": "PM"},
"state_schema": {},
"supported_features": [],
"trigger_schema": {},
"type": "behavior_script",
"version": "0.0.1",
}
FAKE_PRESENCE_MIMICKING_INSTANCE = {
"configuration": {},
"dependees": [], # codespell:ignore dependees
"enabled": True,
"id": "fake_behavior_instance_id_2",
"last_error": "",
"metadata": {"name": "Mimic presence"},
"script_id": "fake_behavior_script_id_2",
"state": {"pm_state": "stopped"},
"status": "running",
"type": "behavior_instance",
}
+78
View File
@@ -16,6 +16,8 @@ from .const import (
FAKE_BEHAVIOR_SCRIPT,
FAKE_BINARY_SENSOR,
FAKE_DEVICE,
FAKE_PRESENCE_MIMICKING_INSTANCE,
FAKE_PRESENCE_MIMICKING_SCRIPT,
FAKE_ZIGBEE_CONNECTIVITY,
)
@@ -198,3 +200,79 @@ async def test_internal_behavior_instance_entity_removed(
await setup_platform(hass, mock_bridge_v2, Platform.SWITCH)
assert entity_registry.async_get(stale_entity.entity_id) is None
@pytest.mark.parametrize(
("pm_state", "expected_state"), [("started", "on"), ("stopped", "off")]
)
async def test_presence_mimicking_switch_state(
hass: HomeAssistant,
mock_bridge_v2: Mock,
v2_resources_test_data: JsonArrayType,
pm_state: str,
expected_state: str,
) -> None:
"""Test the switch follows the run state instead of enabled."""
instance = {**FAKE_PRESENCE_MIMICKING_INSTANCE, "state": {"pm_state": pm_state}}
await mock_bridge_v2.api.load_test_data(
[*v2_resources_test_data, FAKE_PRESENCE_MIMICKING_SCRIPT, instance]
)
await setup_platform(hass, mock_bridge_v2, Platform.SWITCH)
test_entity = hass.states.get("switch.philips_hue_automation_mimic_presence")
assert test_entity is not None
assert test_entity.state == expected_state
@pytest.mark.parametrize(
("service", "expected_trigger"),
[("turn_on", {"start": {}}), ("turn_off", {"stop": {}})],
)
async def test_presence_mimicking_switch_services(
hass: HomeAssistant,
mock_bridge_v2: Mock,
v2_resources_test_data: JsonArrayType,
service: str,
expected_trigger: dict,
) -> None:
"""Test the switch starts and stops instead of touching enabled."""
await mock_bridge_v2.api.load_test_data(
[
*v2_resources_test_data,
FAKE_PRESENCE_MIMICKING_SCRIPT,
FAKE_PRESENCE_MIMICKING_INSTANCE,
]
)
await setup_platform(hass, mock_bridge_v2, Platform.SWITCH)
await hass.services.async_call(
"switch",
service,
{"entity_id": "switch.philips_hue_automation_mimic_presence"},
blocking=True,
)
assert len(mock_bridge_v2.mock_requests) == 1
assert mock_bridge_v2.mock_requests[0]["method"] == "put"
assert mock_bridge_v2.mock_requests[0]["json"] == {"trigger": expected_trigger}
async def test_regular_automation_switch_uses_enabled(
hass: HomeAssistant, mock_bridge_v2: Mock, v2_resources_test_data: JsonArrayType
) -> None:
"""Test an automation without a run state still toggles enabled."""
await mock_bridge_v2.api.load_test_data(v2_resources_test_data)
await setup_platform(hass, mock_bridge_v2, Platform.SWITCH)
await hass.services.async_call(
"switch",
"turn_off",
{"entity_id": "switch.philips_hue_automation_timer_test"},
blocking=True,
)
assert len(mock_bridge_v2.mock_requests) == 1
assert mock_bridge_v2.mock_requests[0]["json"] == {"enabled": False}