Custom integration error creating device with entities

Hi,

Relatively new to python/custom_integration. The overall custom integration works well, it basically creates multiple sensors from an iot device by polling the sensors. I do have the serial number, Mac address and name of the device from polling as well.

The configuration happens in YAML, so no config_flow:

sensor:
    - platform: myiot
      name: "My IOT Device"
      host: 192.168.0.46
      scan_interval: 60
      username: admin
      password: password

Here is some of the relevant code from the sensor.py file:

async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):

    api = IOTApi(config.get(CONF_USERNAME), config.get(CONF_PASSWORD), config.get(CONF_HOST))
    async def async_update_data():
        async with async_timeout.timeout(DEFAULT_TIMEOUT):
            try:
         ...............
    )
    await coordinator.async_refresh()
    await update_unique_ids(hass, coordinator.data)


    device_registry = async_get_device_registry(hass)
    device_registry.async_get_or_create(
        config_entry_id=None, 
        identifiers={(DOMAIN, 'A840411679E7__00_0010_2211_10_0100__000_2_0')},
        manufacturer="My Manufacturer",
        model="My Model",
        name="My Device Name",
        via_device=None, 
        entry_type="device",
        connections=[(CONNECTION_NETWORK_MAC, "A840411679E7")], 
    )

    async_add_entities(
        MyIOTSensor(coordinator, unique_id, endpoint) for unique_id, endpoint in coordinator.data.items()
       )

Since I am using a Platform approach (async_setup_platform), the config_entry.entry_id (which is hass.config_entries.async_get_entry(DOMAIN).entry_id) is not available, and I have set config_entry_id=None. However, Home Assistant does not like that.

I really struggle here, have read the documentation so many times that I don’t know where my head is.

My questions are:

  1. Can I use devices for a custom_integration that is setup without config_flow.py?

  2. If yes, what am I doing wrong, since - after hours of logging - I have come to the conclusion although that the integration uses the platform and reads the config from configuration.yaml, there are no config_entries available.

  3. If no, then I have to go the UI route which means way more complex programming since every device would then have to be setup via config_flow and UI, unless there is a trick/better way almost like a dummy config_flow/UI to trigger the setup of devices?

Thanks for any advice.

M

The proper way to add device information is to add a device_info property to your Entity subclass, e.g.

    @property
    def device_info(self):
        """Return device information about this entity."""
        return {
            "identifiers": {
                # Unique identifiers within a specific domain
                (DOMAIN, self.jelly_cm.server_url)
            },
            "manufacturer": "Jellyfin",
            "model": f"Jellyfin {self.jelly_cm.info['Version']}".rstrip(),
            "name": self.jelly_cm.info['ServerName'],
            "configuration_url": self.jelly_cm.server_url,
        }

Your entities will be “categorized” as belonging to the same device based upon the identifiers.
I don’t think going directly through the device registry is correct.

Thanks Chris for helping out.

I had tried that before, but it would not update the device.

I have subsequently written a logger to output the entity registry and it appears that none of the entities are associated with a device, which is why the device_info() is not working.

Here is the logger code from within my SensorEntity class:

async def async_added_to_hass(self):
        """ When entity is added to hass. """
        self.coordinator.async_add_listener(self.async_write_ha_state)

        entity_reg = er.async_get(self.hass)
        entities = entity_reg.entities

        def format_entities_log(entities):
            formatted_log = []
            for entity_id, entity in entities.items():
                if entity.platform == DOMAIN:
                    entity_details = {
                        'entity_id': entity_id,
                        'domain': entity.domain,
                        'platform': entity.platform,
                        'unique_id': entity.unique_id,
                        'name': entity.original_name,
                        'device_id': entity.device_id,
                    }
                    formatted_log.append(entity_details)
            return formatted_log

        all_entities = {key: value for key, value in entities.items()}
        formatted_log = format_entities_log(all_entities)
        _LOGGER.info(f"Formatted Entities: {formatted_log}")

Now when I look at the logging output I can see that each of my sensors of the custom integration is there, however, none of them have a device_id associated with:

{'entity_id': 'sensor.temperature_sensor_1', 'domain': 'sensor', 'platform': 'myPlatform', 'unique_id': 'A840411679E7__00_0010_2211_10_0100__000_2_0', 'name': 'temperature_sensor_1', 'device_id': None}

I have some 20 sensors, and they all have a unique_id, but none of them have a device_id associated with.

The question is how, within the SensorEntity class when registering new entities/updating existing ones can I create a unique device_id which then groups all those entities?

I somehow appear to be lost here…maybe I just don’t see it?

Thanks for any pointers…

Ah, right, that’s for components implementing config_flow.
Thinking of it, I’m not convinced that devices are supported if you don’t implement it…

so basically unless one uses config_flow, there is no config_entry_id which means devices cannot be registered, which means there is no device_id which can be added as entity.device_id within the SensorEntity class…correct? If so, where do I find this in the documentation as I been reading and searching and trying to figure this out, just seem to be blind here.

So if that is the case, then what is the best way to use a custom_flow without any parameters, or to create a dummy placeholder config entry?

Again, thanks for helping me, very much appreciated.

What I would do is looking for a builtin component doing the same :wink:

Local Calendar seems simple enough. Remove the if user_input is None and it should do what you’re looking for.

core/homeassistant/components/local_calendar/config_flow.py at dev · home-assistant/core (github.com)

Good point, or Shopping List, or even just Sun as a simple integration

I have started with the scaffolding setup from the developer documentation, let’s see how it goes.

And then there is a screaming match between myself and chat GPT4 :wink:

First time Python for me, so let’s see if I get it done. Shouldn’t be that hard!

@koying

Thanks Chris - a few many hours later and I got my first integration semi-done, works well with user/pass/host setup in UI via config_flow, and then device discovery and API calls to fetch all the sensor. Now device properly configure. Once I get it fully done, will post to GitHub.

Thanks for your tips.
M


Just for sake of completeness, I have finally figured out how to do this and published my first integration.

Here is the link:

I do believe that devices presenting sensors only works if one uses custom_flow, which I initially did not use.

However, once custom_flow is implemented, it all works fine.

Thank you for that! :smiley: