From 9e6073099c894e5da2fe689e0fec83e38189b375 Mon Sep 17 00:00:00 2001 From: Niracler Date: Wed, 14 Jan 2026 23:02:25 +0800 Subject: [PATCH] Add button platform to sunricher_dali (#160908) --- .../components/sunricher_dali/__init__.py | 2 +- .../components/sunricher_dali/button.py | 63 ++++++ .../sunricher_dali/snapshots/test_button.ambr | 197 ++++++++++++++++++ .../components/sunricher_dali/test_button.py | 46 ++++ 4 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/sunricher_dali/button.py create mode 100644 tests/components/sunricher_dali/snapshots/test_button.ambr create mode 100644 tests/components/sunricher_dali/test_button.py diff --git a/homeassistant/components/sunricher_dali/__init__.py b/homeassistant/components/sunricher_dali/__init__.py index 2137480cea8..9ed8ec33578 100644 --- a/homeassistant/components/sunricher_dali/__init__.py +++ b/homeassistant/components/sunricher_dali/__init__.py @@ -25,7 +25,7 @@ from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC from .const import CONF_SERIAL_NUMBER, DOMAIN, MANUFACTURER from .types import DaliCenterConfigEntry, DaliCenterData -_PLATFORMS: list[Platform] = [Platform.LIGHT, Platform.SCENE] +_PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.LIGHT, Platform.SCENE] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sunricher_dali/button.py b/homeassistant/components/sunricher_dali/button.py new file mode 100644 index 00000000000..9ba034924bf --- /dev/null +++ b/homeassistant/components/sunricher_dali/button.py @@ -0,0 +1,63 @@ +"""Support for Sunricher DALI device identify button.""" + +from __future__ import annotations + +import logging + +from PySrDaliGateway import Device +from PySrDaliGateway.helper import is_light_device + +from homeassistant.components.button import ButtonDeviceClass, ButtonEntity +from homeassistant.const import EntityCategory +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceInfo +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from .const import DOMAIN, MANUFACTURER +from .entity import DaliDeviceEntity +from .types import DaliCenterConfigEntry + +_LOGGER = logging.getLogger(__name__) + +PARALLEL_UPDATES = 0 + + +async def async_setup_entry( + hass: HomeAssistant, + entry: DaliCenterConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up Sunricher DALI button entities from config entry.""" + devices = entry.runtime_data.devices + + async_add_entities( + DaliCenterIdentifyButton(device) + for device in devices + if is_light_device(device.dev_type) + ) + + +class DaliCenterIdentifyButton(DaliDeviceEntity, ButtonEntity): + """Representation of a Sunricher DALI device identify button.""" + + _attr_device_class = ButtonDeviceClass.IDENTIFY + _attr_entity_category = EntityCategory.CONFIG + _attr_name = None + + def __init__(self, device: Device) -> None: + """Initialize the device identify button.""" + super().__init__(device) + self._device = device + self._attr_unique_id = f"{device.unique_id}_identify" + self._attr_device_info = DeviceInfo( + identifiers={(DOMAIN, device.dev_id)}, + name=device.name, + manufacturer=MANUFACTURER, + model=device.model, + via_device=(DOMAIN, device.gw_sn), + ) + + async def async_press(self) -> None: + """Handle button press to identify device.""" + _LOGGER.debug("Identifying device %s", self._device.dev_id) + self._device.identify() diff --git a/tests/components/sunricher_dali/snapshots/test_button.ambr b/tests/components/sunricher_dali/snapshots/test_button.ambr new file mode 100644 index 00000000000..f5cf00ce896 --- /dev/null +++ b/tests/components/sunricher_dali/snapshots/test_button.ambr @@ -0,0 +1,197 @@ +# serializer version: 1 +# name: test_entities[button.cct_0000_03-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.cct_0000_03', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'sunricher_dali', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '01020000036A242121110E_identify', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[button.cct_0000_03-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'identify', + 'friendly_name': 'CCT 0000-03', + }), + 'context': , + 'entity_id': 'button.cct_0000_03', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[button.dimmer_0000_02-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.dimmer_0000_02', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'sunricher_dali', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '01010000026A242121110E_identify', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[button.dimmer_0000_02-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'identify', + 'friendly_name': 'Dimmer 0000-02', + }), + 'context': , + 'entity_id': 'button.dimmer_0000_02', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[button.hs_color_light-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.hs_color_light', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'sunricher_dali', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '01030000046A242121110E_identify', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[button.hs_color_light-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'identify', + 'friendly_name': 'HS Color Light', + }), + 'context': , + 'entity_id': 'button.hs_color_light', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_entities[button.rgbw_light-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'button', + 'entity_category': , + 'entity_id': 'button.rgbw_light', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': None, + 'platform': 'sunricher_dali', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': None, + 'unique_id': '01040000056A242121110E_identify', + 'unit_of_measurement': None, + }) +# --- +# name: test_entities[button.rgbw_light-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'identify', + 'friendly_name': 'RGBW Light', + }), + 'context': , + 'entity_id': 'button.rgbw_light', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- diff --git a/tests/components/sunricher_dali/test_button.py b/tests/components/sunricher_dali/test_button.py new file mode 100644 index 00000000000..cd8fe64693c --- /dev/null +++ b/tests/components/sunricher_dali/test_button.py @@ -0,0 +1,46 @@ +"""Test the Sunricher DALI button platform.""" + +from unittest.mock import MagicMock + +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS +from homeassistant.const import ATTR_ENTITY_ID, Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from tests.common import MockConfigEntry, snapshot_platform + + +@pytest.fixture +def platforms() -> list[Platform]: + """Fixture to specify which platforms to test.""" + return [Platform.BUTTON] + + +@pytest.mark.usefixtures("init_integration") +async def test_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + entity_registry: er.EntityRegistry, + mock_config_entry: MockConfigEntry, +) -> None: + """Test the button entities.""" + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + +@pytest.mark.usefixtures("init_integration") +async def test_identify_button_press( + hass: HomeAssistant, + mock_devices: list[MagicMock], +) -> None: + """Test pressing the identify button calls device.identify().""" + await hass.services.async_call( + BUTTON_DOMAIN, + SERVICE_PRESS, + {ATTR_ENTITY_ID: "button.dimmer_0000_02"}, + blocking=True, + ) + + mock_devices[0].identify.assert_called_once()