Skip to content
Merged
41 changes: 18 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</p>

<p align="center">
<b>A native Home Assistant custom component providing a sidebar dashboard to manage Google Assistant entity exposure, voice nicknames, regex blocklists, and YAML generation.</b>
<b>A native Home Assistant custom component providing a sidebar dashboard to manage Google Assistant entity exposure, voice nicknames, regex blocklists, and zero-reboot <code>.storage</code> live syncing.</b>
</p>

<p align="center">
Expand All @@ -21,19 +21,22 @@

## Overview

Configuring entities exposed to Google Assistant in Home Assistant traditionally requires manual YAML configuration, tracking entity IDs, maintaining aliases, and reloading configurations.
Self-hosted Google Assistant in Home Assistant traditionally requires manual YAML configuration (`configuration.yaml`), tracking entity IDs, maintaining aliases, and restarting Home Assistant whenever exposed entities change.

**Google Assistant Entity Console** provides a native sidebar dashboard to manage Google Assistant entity configuration visually. It queries the Home Assistant registry (floors, areas, devices, entities), renders dual-layout entity trees, generates voice nicknames using local or cloud models or native Assist conversation agents, and writes structured YAML configuration files (`gaGen_YYMMDD.yaml`) with support for zero-downtime reloads.
**Google Assistant Entity Console v3.0.0** eliminates YAML generation and Home Assistant restarts entirely. It provides a native `.storage`-backed sidebar dashboard that manages your Google Cloud credentials (`project_id`, `service_account`, `report_state`, `secure_devices_pin`), entity exposure, and voice nicknames in memory and syncs live to Google HomeGraph (`devices:requestSync`) with a single click—or automatically with a 15-second debounce.

---

## Key Features

- **100% `.storage`-Native & Zero-Reboot Live Sync (v3.0.0)**:
- Stores Google Cloud credentials, entity sync state, regex blocklists, and AI settings in `.storage/google_assistant_entity_console`.
- Updates Home Assistant Core's `GoogleConfig` in memory on every edit and pushes changes live to Google HomeGraph via **Sync to Google** (or optional 15s debounced auto-sync) without restarting Home Assistant.
- **Auto-Import & One-Click YAML Migration**: Automatically imports existing `project_id`, `service_account` (`SERVICE_ACCOUNT.json`), and `secrets.yaml` values on first run and provides a one-click **Migrate from YAML** button.
- **Native Sidebar Panel**: Integrates into the Home Assistant sidebar as a native panel (`google-assistant-entity-console-panel`).
- **Dual Grouping Layouts**:
- **Slicing Sidebar & Dual Grouping Layouts**:
- **Location-First (Default)**: Group entities by Floor &rarr; Room (Area) &rarr; Domain &rarr; Entity Row.
- **Domain-First (Alternate)**: Group entities by Domain &rarr; Floor &rarr; Room (Area) &rarr; Entity Row.
- **Batch Controls**: Expand or collapse nested sections across an entire floor, room, or domain with a single click.
- **Direct Row Interactions**:
- **Exposure Status**: Toggle entity exposure state (*Exposed*, *Pending Add*, *Pending Remove*, *Not Exposed*).
- **Inline Renaming**: Edit entity display names directly on the row.
Expand All @@ -43,12 +46,17 @@ Configuring entities exposed to Google Assistant in Home Assistant traditionally
- Suggest entity exposure based on natural-language requests (e.g., *"Expose all living room lights and climate controls"*).
- Bulk and room-aware voice nickname generation using local LLMs (Ollama, LocalAI, vLLM), cloud endpoints (OpenAI, OpenRouter, Groq), or Home Assistant Assist conversation agents.
- **Theme Inheritance**: Automatically inherits the active Home Assistant dashboard theme (light, dark, Mushroom, Catppuccin, Nord, etc.) via standard CSS custom properties (`--primary-background-color`, etc.).
- **YAML Generation & Reload**: Generates `gaGen_YYMMDD.yaml` with support for Home Assistant `!secret` and `!include` tags, and triggers configuration reloads via `homeassistant.reload_config_entry`.

---

## Screenshot Previews

### v3.0.0 `.storage` Live Sync Dashboard
![v3.0.0 Live Sync Dashboard](brand/v3_live_sync_dashboard.png)

### v3.0.0 `.storage` Google Assistant & AI Settings Modal
![v3.0.0 Storage Settings Modal](brand/v3_storage_settings_modal.png)

### Location-First Layout (`Floor -> Room -> Domain`)
![Location Grouping Preview](brand/dashboard_location_view.png)

Expand Down Expand Up @@ -86,17 +94,7 @@ Once installed and Home Assistant has restarted:
1. In Home Assistant, navigate to **Settings** > **Devices & Services**.
2. Click **Add Integration** in the bottom-right corner.
3. Search for **Google Assistant Entity Console** and select it.
4. Complete the configuration wizard. A new **Google Sync** panel will appear in your sidebar.

### Linking with `configuration.yaml`

To link the generated YAML configuration file to Home Assistant's built-in `google_assistant:` integration, add an include statement pointing to the generated file inside your `configuration.yaml`:

```yaml
google_assistant: !include gaGen_062226.yaml
```

The console will read, update, and rewrite this file whenever a rebuild is triggered from the sidebar panel.
4. Open the **Google Sync** panel in your sidebar and click **Settings** to verify or configure your Google Cloud `project_id` and `SERVICE_ACCOUNT.json` credentials directly in `.storage` (or click **Migrate from YAML** to import and comment out any legacy `google_assistant:` block in `configuration.yaml`).

---

Expand All @@ -114,19 +112,16 @@ For setup details and custom prompt templates, see the [AI Integration Guide](do

## Documentation

- **[Architecture Overview](docs/ARCHITECTURE.md)**: Details on backend views, entity registry resolution, YAML serialization, regex blocklists, and frontend Web Component architecture.
- **[Architecture Overview](docs/ARCHITECTURE.md)**: Details on backend views, entity registry resolution, `.storage` persistence, regex blocklists, and frontend Web Component architecture.
- **[AI Integration Guide](docs/AI_INTEGRATION.md)**: Configuration details for OpenAI-compatible endpoints, HA Assist agents, and prompt customization.
- **[Dashboard Theme Integration](docs/THEMING.md)**: Mapping of Home Assistant CSS custom properties to Material Design 3 design tokens.

---

## FAQ & Troubleshooting

### Q: Do I need to restart Home Assistant every time I change exposed entities?
**No.** After rebuilding the YAML configuration file in the console, select **Reload Config** to trigger `homeassistant.reload_config_entry` for zero-downtime updates without a core restart.

### Q: Will this overwrite existing secrets or custom YAML tags?
**No.** The backend uses custom PyYAML `SafeDumper` and `SafeLoader` representers to preserve `!secret` and `!include` tags.
### Q: Do I need to restart Home Assistant or maintain YAML files when I change exposed entities?
**No.** Starting in v3.0.0, all entity exposure changes, nicknames, and rooms are updated in Home Assistant's live `GoogleConfig` memory immediately and synced directly to Google HomeGraph via **Sync to Google** (or automatic 15-second debounced sync) with zero restarts and zero YAML files.

### Q: Why are some entities missing from the console?
Google Assistant supports specific Home Assistant domains (`light`, `switch`, `climate`, `lock`, `cover`, `fan`, `vacuum`, `media_player`, `alarm_control_panel`, `camera`, `scene`, `script`, etc.) and binary sensors with specific device classes (`door`, `garage_door`, `lock`, `opening`, `window`, etc.). Unsupported entities are filtered out automatically.
Expand Down
Binary file added brand/v3_live_sync_dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added brand/v3_storage_settings_modal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 47 additions & 14 deletions custom_components/google_assistant_entity_console/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from homeassistant.core import HomeAssistant

from .const import DOMAIN
from .runtime import async_get_runtime
from .store import async_get_store
from .views import (
AIGenerateNicknamesView,
AIGenerateSingleEntityNicknameView,
Expand All @@ -19,45 +21,68 @@
BlocklistAddView,
BlocklistView,
EntitiesView,
GoogleSettingsView,
MigrateYamlView,
RebuildView,
RestartView,
SyncView,
UpdateEntityView,
async_build_entity_config_map,
)

_LOGGER = logging.getLogger(__name__)


async def async_setup(hass: HomeAssistant, config: dict) -> bool:
"""Set up the Google Assistant Entity Console component (legacy YAML fallback)."""
return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up the Google Assistant Entity Console from a config entry."""
_LOGGER.info("Setting up Google Assistant Entity Console from config entry")

# Read version natively via Home Assistant loader for cache busting
from homeassistant.loader import async_get_integration

try:
integration = await async_get_integration(hass, DOMAIN)
version = integration.version
except Exception as err:
_LOGGER.error("Failed to read version from integration loader: %s", err)
version = "1.0.3"

# 1. Register static files directory
static_dir = hass.config.path("custom_components/google_assistant_entity_console/static")
await hass.http.async_register_static_paths([
StaticPathConfig(
"/google_assistant_entity_console/static",
static_dir,
True,
)
])

# 2. Register API views
version = "3.0.0"

# 1. Initialize .storage store and zero-reboot GoogleConfig runtime manager
try:
await async_get_store(hass)
runtime = await async_get_runtime(hass)
entity_config = await async_build_entity_config_map(hass)
await runtime.async_initialize(entity_config)
except Exception as err:
_LOGGER.warning("Deferred runtime initialization: %s", err)

# 2. Register static files directory
static_dir = hass.config.path(
"custom_components/google_assistant_entity_console/static"
)
await hass.http.async_register_static_paths(
[
StaticPathConfig(
"/google_assistant_entity_console/static",
static_dir,
True,
)
]
)

# 3. Register API views
hass.http.register_view(EntitiesView())
hass.http.register_view(UpdateEntityView())
hass.http.register_view(SyncView())
hass.http.register_view(RebuildView())
hass.http.register_view(RestartView())
hass.http.register_view(GoogleSettingsView())
hass.http.register_view(MigrateYamlView())
hass.http.register_view(BlocklistView())
hass.http.register_view(BlocklistAddView())
hass.http.register_view(AISettingsView())
Expand All @@ -67,7 +92,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.http.register_view(AISuggestExposureView())
hass.http.register_view(AIGenerateSingleEntityNicknameView())

# 3. Register Sidebar Panel
# 4. Register Sidebar Panel
async_remove_panel(hass, DOMAIN, warn_if_unknown=False)

async_register_built_in_panel(
Expand All @@ -88,8 +113,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
_LOGGER.info("Unloading Google Assistant Entity Console config entry")
async_remove_panel(hass, DOMAIN, warn_if_unknown=False)
return True


async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle clean removal / uninstallation of the integration entry."""
_LOGGER.info("Removing Google Assistant Entity Console config entry")
async_remove_panel(hass, DOMAIN, warn_if_unknown=False)
hass.data.pop(DOMAIN, None)
4 changes: 4 additions & 0 deletions custom_components/google_assistant_entity_console/const.py
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
DOMAIN = "google_assistant_entity_console"
STORAGE_KEY = "google_assistant_entity_console"
STORAGE_VERSION = 1
DATA_STORE = "store"
DATA_RUNTIME = "runtime"
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "local_polling",
"config_flow": true,
"requirements": [],
"version": "2.0.0"
"version": "3.0.0"
}
Loading
Loading