Dynamically enable entities

I’m building an integration for a power station which has sensors for battery data from the BMS, etc. The unit may also have one or two add-on batteries. I’ve created disabled entities for the extra batteries but am trying to devise a way to automatically enable them if data specific to each entity is detected on the appropriate MQTT topic. What I am missing is the appropriate call to enable the entity after the fact…

I am creating the entity using async_add_entities:

async def async_setup_entry(
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
    """Add entities to Home Assistant."""
    entities = [
            MySensor(
                device,
                "EB1",
                "batt1SoC",
                unit=UNIT_PERCENT,
                device_class="BATTERY",
                state_class="MEASUREMENT",
                dec=2,
                enabled=False,
            ),
            MySensor(
                device,
                "EB2",
                "batt2SoC",
                unit=UNIT_PERCENT,
                device_class="BATTERY",
                state_class="MEASUREMENT",
                dec=2,
                enabled=False,
            ),
    ]
    async_add_entities(entities)

And the base class does this:

class BaseEntity(Entity):
    """Base class for entities."""

    _attr_should_poll = False
    _attr_has_entity_name = True
    _attr_entity_registry_visible_default = True

    def __init__(
        self,
        device: MyDevice,
        entity_name: str,
        mqtt_key: str,
        platform: str,
        json_template: str = None,
        entity_category: str = None,
        enabled: bool = True,
    ) -> None:
        """Initialize the entity."""
        self.device = device
        self.mqtt_key = mqtt_key
        self.json_template = json_template
        self._attr_name = entity_name
        self.entity_id = f"{platform}.{device.device_id}-{slugify(entity_name)}"
        self._attr_unique_id = f"{device.device_sn}-{slugify(entity_name)}"
        self._attr_device_info = device.device_info
        self._attr_entity_category = (
            EntityCategory[entity_category] if entity_category else None
        )
        self._attr_state = None
        self._attr_native_value = None
        self._attr_entity_registry_enabled_default = enabled

This successfully creates a disabled entity in the registry which can be manually enabled in th UI… But I’m having trouble identifying the correct call (or way to code) for enabling the entity, programmatically, when/if my integration detects that data for it is available…

I’m also wondering if using this method will always start these “dynamic” entities in a disabled state when HA is restarted and/or how to detect if they are no longer present and dynamically disable them. Since extra batteries can be attached or detached it would be nice to have an elegant way to handle that. I can create all entities and just allow them to be “unknown” unless/until data arrives for them and return to “unknown” on HA restart but that seems a little ugly to me.

This is my first full scale HA integration so any assistance is appreciated.

Use available for this purpose.
The enabled/disabled is only when the entity is created and then it’s up to the user.

you are referring to self._attr_available ?

So I can create these “dynamic” entities defaulted to disabled in the registry and then only make them “available” when data appears for them and they will always start as “disabled” (when HA restarts) until my integration detects data and makes then “available” again?

The user can override this by making them “enabled” in the UI in which case they will always started enabled? Do I understand correctly?

Yes. self._attr_available is for you to use to tell the user if the entity is available or not eg. You gave data or not in your case.

The other is to have the entity disabled by default. You can not change this later but the user can enable the entity and thereby see the info from it.

1 Like

On a separate but similar note:

    async def async_added_to_hass(self) -> None:
        """Run when entity about to be added."""
        self.device.register_callback(self.mqtt_key, self.async_update_state)

Will the callback here be registered if the entity is created as disabled? If not, I assume I’ll need a separate callback used to make the entity “available” or I’ll need to register the callback outside of async_added_to_hass and handle setting self._attr_enabled = True before calling self.async_write_ha_state() in async_update_state …

No. Only if enabled or when enabled.

1 Like

Ok… so, just to clarify…

  1. Create the entity disabled using:
    _attr_entity_registry_enabled_default = False

  2. Register a call back in init that enables the entity using:

self._attr_available = True
self.async_write_ha_state()

… at which point async_added_to_hass will be called

Do I have that correct now?

And, is async_write_ha_state() the correct way to update the entity to become available or is there a more appropriate call ?

Thanks for the help!

I can’t call async_write_ha_state because the entity is disabled. It would seem I have to somehow enable it before I can update properties like _attr_available or set the state based on received data… Since the entity was already added to the registry when calling async_add_entities from async_setup_entry within the platfrom module (sensor.py for example) this feels like a “chicken an egg” problem…

If I were able to determine the presence of extra batteries when the integration were initialized I could set _attr_entity_registry_enabled_default accordingly at that point but it does not allow for dynamically enabling them (or disabling for that matter). Since this integration is MQTT I cannot simply query for the presence of certain entities at will…

I may be misunderstanding something here though… Am I supposed to create the entities with _attr_entity_registry_enabled_default = True and _attr_available = False and then just turn the available attribute on and off?

I have this working with entities defaulting to _attr_available = False and changing that to true when/if data arrives for the given entitiy. That leaves the entitiy grayed out in the UI when unavailable… This is adequate but not exactly what I was hoping to achieve in terms of pre-creating the these optional entities and only adding them to the UI when/if there is data (indicating they do in fact exist). Instead I have a potential of 28 entities that may forever be “unavailable” if the user has no extra batteries… I suppose the use can decide to disable or hide them if not needed but I was hoping to do that more dynamically for them…

_attr_entity_registry_enabled_default is for using when an entity should be disabled by default and then the user can enable it. It’s for entities that are perhaps not used or needed that much and therefore the developer can give a “recommendation” to have the entity disabled so if the user wants to use it, he can turn it on but perhaps not that commonly used.

_attr_available is for controlling the entity in such manner that is the entity available e.g. there is a connection to the device, data is available etc. etc.

It sounds to be you should skip _attr_entity_registry_enabled_default and only work with _attr_available as if data is available or not.

async_added_to_hass is only executed when an entity is enabled (regardless if available or not) and is the right place to register callbacks etc. as it would anyhow not been needed for disabled entities.
Also async_will_remove_from_hass should be used to remove callbacks as if an entity is removed or disabled.

Thanks for the clarification. I have adapted my code accordingly.