From e51154ae69b590417f78d7ec3caa25cf076ffa3c Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker <joostlek@outlook.com> Date: Fri, 7 Mar 2025 15:46:00 +0100 Subject: [PATCH] Restore SmartThings button event (#140044) * Restore SmartThings button event * Fix --- .../components/smartthings/__init__.py | 32 +++++++++++- homeassistant/components/smartthings/const.py | 2 + tests/components/smartthings/__init__.py | 2 + .../fixtures/device_status/button.json | 21 ++++++++ .../smartthings/fixtures/devices/button.json | 49 +++++++++++++++++++ .../smartthings/snapshots/test_init.ambr | 3 ++ tests/components/smartthings/test_init.py | 36 ++++++++++++-- 7 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 tests/components/smartthings/fixtures/device_status/button.json create mode 100644 tests/components/smartthings/fixtures/devices/button.json diff --git a/homeassistant/components/smartthings/__init__.py b/homeassistant/components/smartthings/__init__.py index 535a409bc8d..3e0e66e890f 100644 --- a/homeassistant/components/smartthings/__init__.py +++ b/homeassistant/components/smartthings/__init__.py @@ -12,6 +12,7 @@ from pysmartthings import ( Attribute, Capability, Device, + DeviceEvent, Scene, SmartThings, SmartThingsAuthenticationFailedError, @@ -29,7 +30,14 @@ from homeassistant.helpers.config_entry_oauth2_flow import ( async_get_config_entry_implementation, ) -from .const import CONF_INSTALLED_APP_ID, CONF_LOCATION_ID, DOMAIN, MAIN, OLD_DATA +from .const import ( + CONF_INSTALLED_APP_ID, + CONF_LOCATION_ID, + DOMAIN, + EVENT_BUTTON, + MAIN, + OLD_DATA, +) _LOGGER = logging.getLogger(__name__) @@ -141,6 +149,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: SmartThingsConfigEntry) rooms=rooms, ) + def handle_button_press(event: DeviceEvent) -> None: + """Handle a button press.""" + if ( + event.capability is Capability.BUTTON + and event.attribute is Attribute.BUTTON + ): + hass.bus.async_fire( + EVENT_BUTTON, + { + "component_id": event.component_id, + "device_id": event.device_id, + "location_id": event.location_id, + "value": event.value, + "name": entry.runtime_data.devices[event.device_id].device.label, + "data": event.data, + }, + ) + + entry.async_on_unload( + client.add_unspecified_device_event_listener(handle_button_press) + ) + entry.async_create_background_task( hass, client.subscribe( diff --git a/homeassistant/components/smartthings/const.py b/homeassistant/components/smartthings/const.py index 23fd48a4e1e..a6d028aed06 100644 --- a/homeassistant/components/smartthings/const.py +++ b/homeassistant/components/smartthings/const.py @@ -32,3 +32,5 @@ CONF_REFRESH_TOKEN = "refresh_token" MAIN = "main" OLD_DATA = "old_data" + +EVENT_BUTTON = "smartthings.button" diff --git a/tests/components/smartthings/__init__.py b/tests/components/smartthings/__init__.py index 6939d3c5dcc..e87d1a8bcdf 100644 --- a/tests/components/smartthings/__init__.py +++ b/tests/components/smartthings/__init__.py @@ -68,6 +68,8 @@ async def trigger_update( value, data, ) + for call in mock.add_unspecified_device_event_listener.call_args_list: + call[0][0](event) for call in mock.add_device_event_listener.call_args_list: if call[0][0] == device_id: call[0][3](event) diff --git a/tests/components/smartthings/fixtures/device_status/button.json b/tests/components/smartthings/fixtures/device_status/button.json new file mode 100644 index 00000000000..93e320bcb7b --- /dev/null +++ b/tests/components/smartthings/fixtures/device_status/button.json @@ -0,0 +1,21 @@ +{ + "components": { + "main": { + "button": { + "button": { + "value": "pushed", + "timestamp": "2025-03-07T12:20:43.363Z" + }, + "numberOfButtons": { + "value": 1, + "timestamp": "2025-03-07T12:20:43.363Z" + }, + "supportedButtonValues": { + "value": ["pushed", "held", "pushed_2x"], + "timestamp": "2025-03-07T12:20:43.363Z" + } + }, + "refresh": {} + } + } +} diff --git a/tests/components/smartthings/fixtures/devices/button.json b/tests/components/smartthings/fixtures/devices/button.json new file mode 100644 index 00000000000..ba993ca6aa7 --- /dev/null +++ b/tests/components/smartthings/fixtures/devices/button.json @@ -0,0 +1,49 @@ +{ + "items": [ + { + "deviceId": "c4bdd19f-85d1-4d58-8f9c-e75ac3cf113b", + "name": "button", + "label": "button", + "manufacturerName": "SmartThingsCommunity", + "presentationId": "238c483a-10e8-359b-b032-1be2b2fcdee7", + "locationId": "88a3a314-f0c8-40b4-bb44-44ba06c9c42f", + "ownerId": "12d4af93-cb68-b108-87f5-625437d7371f", + "components": [ + { + "id": "main", + "label": "main", + "capabilities": [ + { + "id": "button", + "version": 1 + }, + { + "id": "refresh", + "version": 1 + } + ], + "categories": [ + { + "name": "Other", + "categoryType": "manufacturer" + } + ] + } + ], + "createTime": "2025-03-07T12:20:43.273Z", + "profile": { + "id": "b045d731-4d01-35bc-8018-b3da711d8904" + }, + "virtual": { + "name": "button", + "executingLocally": false + }, + "type": "VIRTUAL", + "restrictionTier": 0, + "allowed": null, + "executionContext": "CLOUD", + "relationships": [] + } + ], + "_links": {} +} diff --git a/tests/components/smartthings/snapshots/test_init.ambr b/tests/components/smartthings/snapshots/test_init.ambr index 1918f19911a..5beaf907b70 100644 --- a/tests/components/smartthings/snapshots/test_init.ambr +++ b/tests/components/smartthings/snapshots/test_init.ambr @@ -1,4 +1,7 @@ # serializer version: 1 +# name: test_button_event[button] + <Event smartthings.button[L]: component_id=main, device_id=c4bdd19f-85d1-4d58-8f9c-e75ac3cf113b, location_id=abc, value=pushed, name=button, data=None> +# --- # name: test_devices[aeotec_home_energy_meter_gen5] DeviceRegistryEntrySnapshot({ 'area_id': 'toilet', diff --git a/tests/components/smartthings/test_init.py b/tests/components/smartthings/test_init.py index 3ffe2c11a42..e3d865fc5c8 100644 --- a/tests/components/smartthings/test_init.py +++ b/tests/components/smartthings/test_init.py @@ -2,15 +2,16 @@ from unittest.mock import AsyncMock -from pysmartthings import DeviceResponse, DeviceStatus +from pysmartthings import Attribute, Capability, DeviceResponse, DeviceStatus import pytest from syrupy import SnapshotAssertion +from homeassistant.components.smartthings import EVENT_BUTTON from homeassistant.components.smartthings.const import DOMAIN -from homeassistant.core import HomeAssistant +from homeassistant.core import Event, HomeAssistant from homeassistant.helpers import device_registry as dr -from . import setup_integration +from . import setup_integration, trigger_update from tests.common import MockConfigEntry, load_fixture @@ -33,6 +34,35 @@ async def test_devices( assert device == snapshot +@pytest.mark.parametrize("device_fixture", ["button"]) +async def test_button_event( + hass: HomeAssistant, + devices: AsyncMock, + mock_config_entry: MockConfigEntry, + snapshot: SnapshotAssertion, +) -> None: + """Test button event.""" + await setup_integration(hass, mock_config_entry) + events = [] + + def capture_event(event: Event) -> None: + events.append(event) + + hass.bus.async_listen_once(EVENT_BUTTON, capture_event) + + await trigger_update( + hass, + devices, + "c4bdd19f-85d1-4d58-8f9c-e75ac3cf113b", + Capability.BUTTON, + Attribute.BUTTON, + "pushed", + ) + + assert len(events) == 1 + assert events[0] == snapshot + + @pytest.mark.parametrize("device_fixture", ["da_ac_rac_000001"]) async def test_removing_stale_devices( hass: HomeAssistant, -- GitLab