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