Can't Link Devices to Entities in Home Assistant Custom Integration

57 Views Asked by At

I’m working on a custom integration with the Binance API and struggling to link my devices to their corresponding entities. Despite having both devices and entities created with unique identifiers, the linking is not functioning as expected.

I manage the configuration flow and support adding configurations via configuration.yaml. Below are key parts of my code where I think the issue might be:

**init.py - Device Registration Function:**Here’s the complete register_binance_device function I use for registering devices:

def register_binance_device(hass, coordinator: BinanceCoordinator, device_type, name_suffix, isEntity = False):
    try:
        device_registry = dr.async_get(hass)
        device_info = coordinator.get_device_info(device_type, name_suffix, isEntity)

        device_args = {
            "config_entry_id": coordinator.config_entry.entry_id,
            "identifiers": device_info["identifiers"],
            "manufacturer": device_info["manufacturer"],
            "name": device_info["name"],
            "model": device_info["model"],
            "configuration_url": device_info["configuration_url"],
        }

        if hasattr(device_info, 'via_device') and device_info.via_device:
            device_args["via_device"] = device_info.via_device

        device_registry.async_get_or_create(**device_args )
        
    except Exception as e:
        _LOGGER.error(f"Error registering Binance {device_type} device: {e}")

**init.py - BinanceCoordinator Device Info Methods:**The BinanceCoordinator class provides device information, which is crucial for linking. Here’s an example for balance devices:

  class BinanceCoordinator(DataUpdateCoordinator):
        def get_device_info(self, device_type: str, name_suffix: str, isEntity = True) -> DeviceInfo:
            identifiers={(DOMAIN, str(self.config_entry.entry_id) + "-" + device_type)}
            device_info_args = {
                "identifiers": identifiers,
                "manufacturer": "Binance",
                "name": f"{self.conf_name} Binance {name_suffix}",
                "model": f"Binance {name_suffix}",
                "configuration_url": "https://www.binance.com",
                "sw_version": "1.0",
                "entry_type": "service"
            }
            if device_type != "account":
                device_info_args["via_device"] = {(DOMAIN, str(self.config_entry.entry_id) + "-account")}
            if isEntity:
                return DeviceInfo(**device_info_args)
            else: 
                return device_info_args
    
        @property
        def device_info_balances(self) -> DeviceInfo:
            return self.get_device_info("balances", "Spot Balances")

**sensor.py - Sensor Entity Implementation:**My sensor entities, such as BinanceSensor, use the device_info property from BinanceCoordinator to link to a device. Here’s the implementation:

class BinanceSensor(CoordinatorEntity, SensorEntity):
    def __init__(self, coordinator, name, balance):
        # Initialization code
        super().__init__(coordinator)
        self._name = f"{name} {balance['asset']} Balance"
        # ...

    @property
    def device_info(self):
        """Return device specific attributes."""
        return self._coordinator.device_info_balances

    # Additional properties and methods...

**sensor.py async_setup_platform implementation called by async_load_platform in init.py

async def async_setup_platform(hass, config, async_add_entities: AddEntitiesCallback, discovery_info=None):
    """Setup the Binance sensors."""
    if discovery_info is None:
        return
    try:
        entry_id = discovery_info.get('entry_id')
        sensor_type = discovery_info.get('sensor_type')
        conf_name = discovery_info.get('conf_name')

        if not all(is_valid_string(val) for val in [entry_id, sensor_type, conf_name]):
            _LOGGER.error("Invalid configuration data.")
            return

        coordinator = hass.data[DOMAIN].get(entry_id)
        if coordinator is None:
            _LOGGER.error("Coordinator for entry_id not found.")
            return

        sensors = []

        if sensor_type == 'balance':
            balances = coordinator.data.get("balances", [])
            for balance in balances:
                if not isinstance(balance, dict) or not all(key in balance for key in ["asset", "free", "locked"]):
                    _LOGGER.error(f"Invalid balance data: {balance}")
                    continue
                sensor = BinanceSensor(coordinator, conf_name, balance)
                _LOGGER.warning(f"Device Info {sensor.device_info} sensors")

                if sensor.is_valid:
                    sensors.append(sensor)

        elif sensor_type == 'exchange':
            tickers = coordinator.data.get("tickers", {})
            for symbol, ticker in tickers.items():
                if not isinstance(ticker, dict) or "price" not in ticker:
                    _LOGGER.error(f"Invalid ticker data for symbol {symbol}: {ticker}")
                    continue
                sensor = BinanceExchangeSensor(coordinator, conf_name, ticker)
                if sensor.is_valid:
                    sensors.append(sensor)

        _LOGGER.warning(f"async_add_entities called with {len(sensors)} sensors")
        async_add_entities(sensors, True)

    except ValueError as ve:
        _LOGGER.error(f"Value error during sensor setup: {ve}")
    except TypeError as te:
        _LOGGER.error(f"Type error during sensor setup: {te}")
    except Exception as e:
        _LOGGER.error(f"Unexpected error during sensor setup: {e}")

Despite this setup, devices and entities are not linking as intended. I suspect the issue might be in how device_info is used or in the device registration process. I’d greatly appreciate any guidance or suggestions on what might be going wrong.

I checked the thread Custom integration error creating device with entities which had a similar problem to mine, I looked at the code in question but I couldn’t identify anything.

I’m going through async_setup_platform but the more I dig, the less I see people doing this.

Thank you for your help!

0

There are 0 best solutions below