-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Description
Feedback
When using the built-in Rollease Acmeda Automate Pulse Hub integration, hubs were not discovered automatically via the "Add Integration" flow in a Home Assistant environment that is connected to multiple VLANs. As a result, auto-discovery did not work and there was no manual IP entry option available in the UI, making setup impossible in this scenario.
It is not clear if this is a common case, but in this environment a manual test using @atmurray/aiopulse's demo.py was able to discover the hub. The test worked after adjusting /usr/local/lib/python3.13/site-packages/aiopulse/transport.py
to use a specific broadcast address:
async def connect(self, host="172.27.47.255"):
Note: this adjustment did NOT cause the integration to auto-discover the hub... I eventually abandoned further troubleshooting down that path.
Request:
- Please add a manual IP entry option to the Acmeda integration config flow, so users on networks where UDP discovery does not work (such as multi-VLAN environments) can add their hubs by IP address.
Context:
- Tested by directly editing
/usr/src/homeassistant/homeassistant/components/acmeda/config_flow.py
and confirming that adding a manual IP entry step (withaiopulse.Hub(host); await hub.connect()
) allows the integration to add my hub. Integration appears to be functional (albeit cover's positions are not displayed - which I understand to be a general limitation of the library or integration) - The full working
config_flow.py
is included below for reference:
"""Config flow for Rollease Acmeda Automate Pulse Hub (custom integration)."""
from __future__ import annotations
import logging
from asyncio import timeout
from contextlib import suppress
from typing import Any
import voluptuous as vol
import aiopulse
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_ID
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
class AcmedaFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle a Acmeda config flow."""
VERSION = 1
def __init__(self) -> None:
"""Initialize the config flow."""
self.discovered_hubs: dict[str, aiopulse.Hub] | None = None
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initialized by the user."""
_LOGGER.debug("Starting Acmeda config flow, user_input: %s", user_input)
# If user selects a discovered hub
if (
user_input is not None
and self.discovered_hubs is not None
and user_input.get(CONF_ID) in self.discovered_hubs
):
_LOGGER.debug("User selected discovered hub: %s", user_input[CONF_ID])
return await self.async_create(self.discovered_hubs[user_input[CONF_ID]])
# Already configured hosts
already_configured = {
entry.unique_id for entry in self._async_current_entries()
}
# Discover hubs on the network
hubs: list[aiopulse.Hub] = []
try:
with suppress(TimeoutError):
_LOGGER.info("Discovering Acmeda hubs via aiopulse.Hub.discover()...")
async with timeout(10):
hubs = [
hub
async for hub in aiopulse.Hub.discover()
if hub.id not in already_configured
]
except Exception as exc:
_LOGGER.error("Exception during discovery: %s", exc, exc_info=True)
if not hubs:
_LOGGER.info("No hubs discovered, showing manual entry form")
return await self.async_step_manual()
if len(hubs) == 1:
_LOGGER.info("Only one hub discovered, proceeding with: %s", hubs[0].host)
return await self.async_create(hubs[0])
# Multiple hubs found, let user select
self.discovered_hubs = {hub.id: hub for hub in hubs}
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_ID): vol.In(
{hub.id: f"{hub.id} ({hub.host})" for hub in hubs}
)
}
),
)
async def async_step_manual(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Allow manual entry of the hub host."""
errors = {}
if user_input is not None:
host = user_input[CONF_HOST]
try:
_LOGGER.info("Manual step: Creating aiopulse.Hub with host %s", host)
hub = aiopulse.Hub(host)
await hub.connect() # For aiopulse 0.4.x, use connect() to verify
_LOGGER.info("Manual step: Successfully connected to hub %s (%s)", hub.id, hub.host)
await self.async_set_unique_id(hub.id, raise_on_progress=False)
return self.async_create_entry(title=hub.id, data={CONF_HOST: host})
except Exception as exc:
_LOGGER.error("Manual step: Failed to connect to hub at %s: %s", host, exc, exc_info=True)
errors["base"] = "cannot_connect"
return self.async_show_form(
step_id="manual",
data_schema=vol.Schema(
{
vol.Required(CONF_HOST): str,
}
),
errors=errors,
)
async def async_create(self, hub: aiopulse.Hub) -> ConfigFlowResult:
"""Create the Acmeda Hub entry."""
_LOGGER.info("Creating config entry for hub: %s (%s)", hub.id, hub.host)
await self.async_set_unique_id(hub.id, raise_on_progress=False)
return self.async_create_entry(title=hub.id, data={CONF_HOST: hub.host})
Full disclosure... I generated this issue with AI but reviewed, edited and amended it afterwards.
URL
https://www.home-assistant.io/integrations/acmeda/
Version
2025.6.3
Additional information
No response