diff --git a/homeassistant/components/isy994/__init__.py b/homeassistant/components/isy994/__init__.py
index 05e44ea4fdd..e75cb7659c0 100644
--- a/homeassistant/components/isy994/__init__.py
+++ b/homeassistant/components/isy994/__init__.py
@@ -11,6 +11,7 @@ from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import config_validation as cv
+import homeassistant.helpers.device_registry as dr
from homeassistant.helpers.typing import ConfigType
from .const import (
@@ -26,6 +27,7 @@ from .const import (
ISY994_ISY,
ISY994_NODES,
ISY994_PROGRAMS,
+ MANUFACTURER,
SUPPORTED_PLATFORMS,
SUPPORTED_PROGRAM_PLATFORMS,
UNDO_UPDATE_LISTENER,
@@ -156,6 +158,7 @@ async def async_setup_entry(
_LOGGER.info(repr(isy.clock))
hass_isy_data[ISY994_ISY] = isy
+ await _async_get_or_create_isy_device_in_registry(hass, entry, isy)
# Load platforms for the devices in the ISY controller that we support.
for platform in SUPPORTED_PLATFORMS:
@@ -203,6 +206,22 @@ def _async_import_options_from_data_if_missing(
hass.config_entries.async_update_entry(entry, options=options)
+async def _async_get_or_create_isy_device_in_registry(
+ hass: HomeAssistant, entry: config_entries.ConfigEntry, isy
+) -> None:
+ device_registry = await dr.async_get_registry(hass)
+
+ device_registry.async_get_or_create(
+ config_entry_id=entry.entry_id,
+ connections={(dr.CONNECTION_NETWORK_MAC, isy.configuration["uuid"])},
+ identifiers={(DOMAIN, isy.configuration["uuid"])},
+ manufacturer=MANUFACTURER,
+ name=isy.configuration["name"],
+ model=isy.configuration["model"],
+ sw_version=isy.configuration["firmware"],
+ )
+
+
async def async_unload_entry(
hass: HomeAssistant, entry: config_entries.ConfigEntry
) -> bool:
diff --git a/homeassistant/components/isy994/entity.py b/homeassistant/components/isy994/entity.py
index 9a1444e61d3..b1642b55672 100644
--- a/homeassistant/components/isy994/entity.py
+++ b/homeassistant/components/isy994/entity.py
@@ -5,6 +5,8 @@ from pyisy.constants import (
EMPTY_TIME,
EVENT_PROPS_IGNORED,
ISY_VALUE_UNKNOWN,
+ PROTO_GROUP,
+ PROTO_ZWAVE,
)
from pyisy.helpers import NodeProperty
@@ -12,6 +14,8 @@ from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import Dict
+from .const import DOMAIN
+
class ISYEntity(Entity):
"""Representation of an ISY994 device."""
@@ -53,6 +57,53 @@ class ISYEntity(Entity):
self.hass.bus.fire("isy994_control", event_data)
+ @property
+ def device_info(self):
+ """Return the device_info of the device."""
+ if hasattr(self._node, "protocol") and self._node.protocol == PROTO_GROUP:
+ # not a device
+ return None
+ uuid = self._node.isy.configuration["uuid"]
+ node = self._node
+ basename = self.name
+
+ if hasattr(self._node, "parent_node") and self._node.parent_node is not None:
+ # This is not the parent node, get the parent node.
+ node = self._node.parent_node
+ basename = node.name
+
+ device_info = {
+ "name": basename,
+ "identifiers": {},
+ "model": "Unknown",
+ "manufacturer": "Unknown",
+ "via_device": (DOMAIN, uuid),
+ }
+
+ if hasattr(node, "address"):
+ device_info["name"] += f" ({node.address})"
+ if hasattr(node, "primary_node"):
+ device_info["identifiers"] = {(DOMAIN, f"{uuid}_{node.address}")}
+ # ISYv5 Device Types
+ if hasattr(node, "node_def_id") and node.node_def_id is not None:
+ device_info["model"] = node.node_def_id
+ # Numerical Device Type
+ if hasattr(node, "type") and node.type is not None:
+ device_info["model"] += f" {node.type}"
+ if hasattr(node, "protocol"):
+ device_info["manufacturer"] = node.protocol
+ if node.protocol == PROTO_ZWAVE:
+ # Get extra information for Z-Wave Devices
+ device_info["manufacturer"] += f" MfrID:{node.zwave_props.mfr_id}"
+ device_info["model"] += (
+ f" Type:{node.zwave_props.devtype_gen} "
+ f"ProductTypeID:{node.zwave_props.prod_type_id} "
+ f"ProductID:{node.zwave_props.product_id}"
+ )
+ # Note: sw_version is not exposed by the ISY for the individual devices.
+
+ return device_info
+
@property
def unique_id(self) -> str:
"""Get the unique identifier of the device."""
diff --git a/tests/components/isy994/test_config_flow.py b/tests/components/isy994/test_config_flow.py
index c9c914b5a79..c1ca0ab4b5f 100644
--- a/tests/components/isy994/test_config_flow.py
+++ b/tests/components/isy994/test_config_flow.py
@@ -74,7 +74,7 @@ async def test_form(hass: HomeAssistantType):
PATCH_ASYNC_SETUP_ENTRY, return_value=True,
) as mock_setup_entry:
isy_conn = mock_connection_class.return_value
- isy_conn.get_config.return_value = ""
+ isy_conn.get_config.return_value = None
mock_config_class.return_value = MOCK_VALIDATED_RESPONSE
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], MOCK_USER_INPUT,
@@ -154,7 +154,7 @@ async def test_form_existing_config_entry(hass: HomeAssistantType):
PATCH_CONNECTION
) as mock_connection_class:
isy_conn = mock_connection_class.return_value
- isy_conn.get_config.return_value = ""
+ isy_conn.get_config.return_value = None
mock_config_class.return_value = MOCK_VALIDATED_RESPONSE
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"], MOCK_USER_INPUT,
@@ -170,7 +170,7 @@ async def test_import_flow_some_fields(hass: HomeAssistantType) -> None:
PATCH_ASYNC_SETUP_ENTRY, return_value=True,
):
isy_conn = mock_connection_class.return_value
- isy_conn.get_config.return_value = ""
+ isy_conn.get_config.return_value = None
mock_config_class.return_value = MOCK_VALIDATED_RESPONSE
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=MOCK_IMPORT_BASIC_CONFIG,
@@ -190,7 +190,7 @@ async def test_import_flow_all_fields(hass: HomeAssistantType) -> None:
PATCH_ASYNC_SETUP_ENTRY, return_value=True,
):
isy_conn = mock_connection_class.return_value
- isy_conn.get_config.return_value = ""
+ isy_conn.get_config.return_value = None
mock_config_class.return_value = MOCK_VALIDATED_RESPONSE
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=MOCK_IMPORT_FULL_CONFIG,