Update changelog for version 0.1.9, enhance Lovelace resource registration, improve frontend registration retries, and update minimum Home Assistant version
This commit is contained in:
@@ -2,6 +2,25 @@
|
|||||||
|
|
||||||
All notable changes to the OpenClaw Home Assistant Integration will be documented in this file.
|
All notable changes to the OpenClaw Home Assistant Integration will be documented in this file.
|
||||||
|
|
||||||
|
## [0.1.9] - 2026-02-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Updated Lovelace resource registration to use Home Assistant 2026.2 storage API (`hass.data[LOVELACE_DATA].resources`) with legacy fallback.
|
||||||
|
- Prevented silent resource-registration failure caused by reading the old `hass.data["lovelace"]` key only.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated `hacs.json` minimum Home Assistant version to `2026.2.0`.
|
||||||
|
|
||||||
|
## [0.1.8] - 2026-02-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Removed false-positive config-flow warning for `enable_openai_api=false` when Supervisor options are missing or use a different schema.
|
||||||
|
- Frontend auto-registration no longer gets stuck after an early startup failure.
|
||||||
|
- Card resource registration now retries for longer and can recover on integration reload.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Frontend registration task is now de-duplicated while running and marked complete only after successful Lovelace resource creation.
|
||||||
|
|
||||||
## [0.1.7] - 2026-02-20
|
## [0.1.7] - 2026-02-20
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ from typing import Any
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
try:
|
||||||
|
from homeassistant.components.lovelace.const import LOVELACE_DATA
|
||||||
|
except ImportError: # pragma: no cover
|
||||||
|
LOVELACE_DATA = "lovelace"
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
|
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||||
@@ -175,15 +180,21 @@ async def _async_register_frontend(hass: HomeAssistant) -> None:
|
|||||||
Called as a fire-and-forget task from async_setup_entry.
|
Called as a fire-and-forget task from async_setup_entry.
|
||||||
Wrapped entirely in try/except so it can NEVER crash the integration.
|
Wrapped entirely in try/except so it can NEVER crash the integration.
|
||||||
"""
|
"""
|
||||||
frontend_key = f"{DOMAIN}_frontend_registration_started"
|
frontend_done_key = f"{DOMAIN}_frontend_registered"
|
||||||
if hass.data.get(frontend_key):
|
frontend_task_key = f"{DOMAIN}_frontend_registration_task"
|
||||||
|
|
||||||
|
if hass.data.get(frontend_done_key):
|
||||||
|
return
|
||||||
|
|
||||||
|
existing_task = hass.data.get(frontend_task_key)
|
||||||
|
if existing_task and not existing_task.done():
|
||||||
return
|
return
|
||||||
hass.data[frontend_key] = True
|
|
||||||
|
|
||||||
async def _register_with_retries() -> None:
|
async def _register_with_retries() -> None:
|
||||||
for _ in range(12):
|
for _ in range(60):
|
||||||
url = await _async_register_static_path(hass)
|
url = await _async_register_static_path(hass)
|
||||||
if url and await _async_add_lovelace_resource(hass, url):
|
if url and await _async_add_lovelace_resource(hass, url):
|
||||||
|
hass.data[frontend_done_key] = True
|
||||||
return
|
return
|
||||||
await asyncio.sleep(5)
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
@@ -193,9 +204,17 @@ async def _async_register_frontend(hass: HomeAssistant) -> None:
|
|||||||
_CARD_URL,
|
_CARD_URL,
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.async_create_task(_register_with_retries())
|
task = hass.async_create_task(_register_with_retries())
|
||||||
|
hass.data[frontend_task_key] = task
|
||||||
|
|
||||||
|
def _clear_task(_fut: asyncio.Future) -> None:
|
||||||
|
hass.data.pop(frontend_task_key, None)
|
||||||
|
|
||||||
|
task.add_done_callback(_clear_task)
|
||||||
|
|
||||||
async def _on_ha_started(_event) -> None:
|
async def _on_ha_started(_event) -> None:
|
||||||
|
if hass.data.get(frontend_done_key):
|
||||||
|
return
|
||||||
await _register_with_retries()
|
await _register_with_retries()
|
||||||
|
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _on_ha_started)
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _on_ha_started)
|
||||||
@@ -230,15 +249,20 @@ async def _async_register_static_path(hass: HomeAssistant) -> str | None:
|
|||||||
|
|
||||||
async def _async_add_lovelace_resource(hass: HomeAssistant, url: str) -> bool:
|
async def _async_add_lovelace_resource(hass: HomeAssistant, url: str) -> bool:
|
||||||
"""Add the card URL to Lovelace's resource store if not already present."""
|
"""Add the card URL to Lovelace's resource store if not already present."""
|
||||||
# Lovelace stores resources in hass.data["lovelace"]["resources"].
|
# Lovelace stores resources in hass.data[LOVELACE_DATA].resources on
|
||||||
# It is a ResourceStorageCollection with:
|
# modern Home Assistant versions (fallback to legacy dict key below).
|
||||||
|
# Resource collection supports:
|
||||||
# .async_items() → list of {"id", "res_type", "url"} dicts
|
# .async_items() → list of {"id", "res_type", "url"} dicts
|
||||||
# .async_create_item(data) → persists a new resource
|
# .async_create_item(data) → persists a new resource
|
||||||
lovelace_data = hass.data.get("lovelace")
|
lovelace_data = hass.data.get(LOVELACE_DATA) or hass.data.get("lovelace")
|
||||||
if not lovelace_data:
|
if not lovelace_data:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if isinstance(lovelace_data, dict):
|
||||||
resource_collection = lovelace_data.get("resources")
|
resource_collection = lovelace_data.get("resources")
|
||||||
|
else:
|
||||||
|
resource_collection = getattr(lovelace_data, "resources", None)
|
||||||
|
|
||||||
if resource_collection is None:
|
if resource_collection is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -163,9 +163,13 @@ async def _async_try_discover_addon(hass: HomeAssistant) -> dict[str, Any] | Non
|
|||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Warn early if the OpenAI-compatible API is disabled — /v1/models will return
|
# Warn only when Supervisor explicitly reports the option as disabled.
|
||||||
# HTML and the connection probe will fail with a misleading error.
|
# If the key is missing (older/newer addon schema), do not warn.
|
||||||
if not addon_options.get("enable_openai_api", False):
|
enable_openai_api = addon_options.get("enable_openai_api")
|
||||||
|
if enable_openai_api is None and isinstance(addon_options.get("gateway"), dict):
|
||||||
|
enable_openai_api = addon_options["gateway"].get("enable_openai_api")
|
||||||
|
|
||||||
|
if enable_openai_api is False:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Addon option 'enable_openai_api' is false. "
|
"Addon option 'enable_openai_api' is false. "
|
||||||
"The integration requires this to be enabled. "
|
"The integration requires this to be enabled. "
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"issue_tracker": "https://github.com/techartdev/OpenClawHomeAssistant/issues",
|
"issue_tracker": "https://github.com/techartdev/OpenClawHomeAssistant/issues",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"version": "0.1.7",
|
"version": "0.1.9",
|
||||||
"dependencies": ["conversation"],
|
"dependencies": ["conversation"],
|
||||||
"after_dependencies": ["hassio", "lovelace"]
|
"after_dependencies": ["hassio", "lovelace"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
"render_readme": true,
|
"render_readme": true,
|
||||||
"domains": ["sensor", "binary_sensor", "conversation"],
|
"domains": ["sensor", "binary_sensor", "conversation"],
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"homeassistant": "2024.1.0"
|
"homeassistant": "2026.2.0"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user