1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-19 18:38:58 +00:00

Enhance Sunricher DALI with update gateway IP from DHCP discovery (#157809)

Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
Niracler
2025-12-16 22:19:37 +08:00
committed by GitHub
parent 5f4f07803b
commit 5796b4c0d9
9 changed files with 249 additions and 6 deletions

View File

@@ -18,6 +18,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from .const import CONF_SERIAL_NUMBER, DOMAIN, MANUFACTURER
from .types import DaliCenterConfigEntry, DaliCenterData
@@ -58,6 +59,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: DaliCenterConfigEntry) -
dev_reg = dr.async_get(hass)
dev_reg.async_get_or_create(
config_entry_id=entry.entry_id,
connections={(CONNECTION_NETWORK_MAC, gw_sn)},
identifiers={(DOMAIN, gw_sn)},
manufacturer=MANUFACTURER,
name=gateway.name,

View File

@@ -18,11 +18,13 @@ from homeassistant.const import (
CONF_PORT,
CONF_USERNAME,
)
from homeassistant.helpers.device_registry import format_mac
from homeassistant.helpers.selector import (
SelectOptionDict,
SelectSelector,
SelectSelectorConfig,
)
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from .const import CONF_SERIAL_NUMBER, DOMAIN
@@ -132,3 +134,15 @@ class DaliCenterConfigFlow(ConfigFlow, domain=DOMAIN):
),
errors=errors,
)
async def async_step_dhcp(
self, discovery_info: DhcpServiceInfo
) -> ConfigFlowResult:
"""Handle DHCP discovery to update existing entries."""
mac_address = format_mac(discovery_info.macaddress)
serial_number = mac_address.replace(":", "").upper()
await self.async_set_unique_id(serial_number)
self._abort_if_unique_id_configured(updates={CONF_HOST: discovery_info.ip})
return self.async_abort(reason="no_dhcp_flow")

View File

@@ -3,6 +3,11 @@
"name": "Sunricher DALI",
"codeowners": ["@niracler"],
"config_flow": true,
"dhcp": [
{
"registered_devices": true
}
],
"documentation": "https://www.home-assistant.io/integrations/sunricher_dali",
"iot_class": "local_push",
"quality_scale": "bronze",

View File

@@ -40,8 +40,10 @@ rules:
# Gold
devices: done
diagnostics: todo
discovery-update-info: todo
discovery: todo
discovery-update-info: done
discovery:
status: exempt
comment: Device has no way to be discovered.
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo

View File

@@ -1,13 +1,13 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
"no_dhcp_flow": "No DHCP flow"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"discovery_failed": "Failed to discover Sunricher DALI gateways on the network",
"no_devices_found": "No Sunricher DALI gateways found on the network",
"unknown": "[%key:common::config_flow::error::unknown%]"
"no_devices_found": "No Sunricher DALI gateways found on the network"
},
"step": {
"select_gateway": {

View File

@@ -829,6 +829,10 @@ DHCP: Final[list[dict[str, str | bool]]] = [
"hostname": "my[45]50*",
"macaddress": "001E0C*",
},
{
"domain": "sunricher_dali",
"registered_devices": True,
},
{
"domain": "tado",
"hostname": "tado*",

View File

@@ -0,0 +1,154 @@
# serializer version: 1
# name: test_devices
list([
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': None,
'connections': set({
tuple(
'mac',
'6a:24:21:21:11:0e',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'sunricher_dali',
'6A242121110E',
),
}),
'labels': set({
}),
'manufacturer': 'Sunricher',
'model': 'SR-GW-EDA',
'model_id': None,
'name': 'Test Gateway',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': '6A242121110E',
'sw_version': None,
'via_device_id': None,
}),
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': None,
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'sunricher_dali',
'01010000026A242121110E',
),
}),
'labels': set({
}),
'manufacturer': 'Sunricher',
'model': 'DALI DT6 Dimmable Driver',
'model_id': None,
'name': 'Dimmer 0000-02',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'sw_version': None,
'via_device_id': <ANY>,
}),
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': None,
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'sunricher_dali',
'01020000036A242121110E',
),
}),
'labels': set({
}),
'manufacturer': 'Sunricher',
'model': 'DALI DT8 Tc Dimmable Driver',
'model_id': None,
'name': 'CCT 0000-03',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'sw_version': None,
'via_device_id': <ANY>,
}),
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': None,
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'sunricher_dali',
'01030000046A242121110E',
),
}),
'labels': set({
}),
'manufacturer': 'Sunricher',
'model': 'DALI HS Color Driver',
'model_id': None,
'name': 'HS Color Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'sw_version': None,
'via_device_id': <ANY>,
}),
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'config_entries_subentries': <ANY>,
'configuration_url': None,
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'identifiers': set({
tuple(
'sunricher_dali',
'01040000056A242121110E',
),
}),
'labels': set({
}),
'manufacturer': 'Sunricher',
'model': 'DALI RGBW Driver',
'model_id': None,
'name': 'RGBW Light',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'sw_version': None,
'via_device_id': <ANY>,
}),
])
# ---

View File

@@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, MagicMock
from PySrDaliGateway.exceptions import DaliGatewayError
from homeassistant.components.sunricher_dali.const import CONF_SERIAL_NUMBER, DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.config_entries import SOURCE_DHCP, SOURCE_USER
from homeassistant.const import (
CONF_HOST,
CONF_NAME,
@@ -15,6 +15,7 @@ from homeassistant.const import (
)
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
from tests.common import MockConfigEntry
@@ -219,3 +220,43 @@ async def test_discovery_unique_id_already_configured(
assert result.get("type") is FlowResultType.ABORT
assert result.get("reason") == "already_configured"
async def test_dhcp_updates_existing_entry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test DHCP discovery updates IP of existing entry."""
mock_config_entry.add_to_hass(hass)
assert mock_config_entry.data[CONF_HOST] != "192.168.1.200"
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_DHCP},
data=DhcpServiceInfo(
ip="192.168.1.200",
macaddress="6a242121110e",
hostname="dali-gateway",
),
)
assert result.get("type") is FlowResultType.ABORT
assert result.get("reason") == "already_configured"
assert mock_config_entry.data[CONF_HOST] == "192.168.1.200"
async def test_dhcp_unknown_device(hass: HomeAssistant) -> None:
"""Test DHCP discovery of unknown device aborts."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_DHCP},
data=DhcpServiceInfo(
ip="192.168.1.100",
macaddress="aabbccddeeff",
hostname="unknown-gateway",
),
)
assert result.get("type") is FlowResultType.ABORT
assert result.get("reason") == "no_dhcp_flow"

View File

@@ -3,9 +3,11 @@
from unittest.mock import MagicMock
from PySrDaliGateway.exceptions import DaliGatewayError
from syrupy.assertion import SnapshotAssertion
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
import homeassistant.helpers.device_registry as dr
from tests.common import MockConfigEntry
@@ -26,6 +28,25 @@ async def test_setup_entry_success(
mock_gateway.connect.assert_called_once()
async def test_devices(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_gateway: MagicMock,
device_registry: dr.DeviceRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test that devices are registered correctly."""
mock_config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
devices = dr.async_entries_for_config_entry(
device_registry, mock_config_entry.entry_id
)
assert devices == snapshot
async def test_setup_entry_connection_error(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,