commit 5ea269d952744d2cae9a0de0dae7fd7e53d72ca8 Author: Your Name Date: Wed Dec 17 00:37:47 2025 -0600 initial release - assistd REST API for assist-exposed entities diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4ef26ad --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 sudoxnym + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ca032c --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# ha-assistd + +home assistant custom component that exposes assist-exposed entities via REST API. + +## why + +home assistant has no REST endpoint to get entities exposed to assist. this makes it hard to build external tools that respect your expose settings. + +assistd fixes that. one endpoint. only the entities you've chosen to expose. + +## installation + +### manual + +1. copy `custom_components/assistd` to your HA config directory +2. add to `configuration.yaml`: + ```yaml + assistd: + ``` +3. restart home assistant + +### hacs (coming soon) + +add as custom repository: `https://github.com/sudoxnym/ha-assistd` + +## usage + +```bash +curl -s "http://YOUR_HA:8123/api/assistd" \ + -H "Authorization: Bearer YOUR_LONG_LIVED_TOKEN" +``` + +### response + +```json +{ + "count": 4, + "entities": [ + { + "entity_id": "light.bedroom", + "name": "Bedroom Light", + "aliases": ["bedroom", "bed light"], + "domain": "light", + "platform": "hue", + "state": "on", + "area_id": "bedroom" + }, + { + "entity_id": "switch.fan", + "name": "Ceiling Fan", + "aliases": [], + "domain": "switch", + "platform": "zha", + "state": "off", + "area_id": "living_room" + } + ] +} +``` + +### fields + +| field | description | +|-------|-------------| +| `entity_id` | full entity id | +| `name` | friendly name | +| `aliases` | voice aliases configured in HA | +| `domain` | entity domain (light, switch, climate, etc) | +| `platform` | integration that owns this entity | +| `state` | current state | +| `area_id` | area assignment (if any) | + +## use cases + +- build LLM-powered voice assistants that respect your expose settings +- create external dashboards showing only exposed entities +- sync exposed entities to external systems +- audit what's exposed to assist + +## example: natural language control + +pair with an LLM to build a CLI controller: + +```bash +#!/bin/bash +# get exposed entities, send to LLM with user command, execute result +entities=$(curl -s "http://ha:8123/api/assistd" -H "Authorization: Bearer $TOKEN") +# ... LLM interprets "turn off the lights" → light.turn_off on light.bedroom +``` + +## license + +MIT diff --git a/custom_components/assistd/__init__.py b/custom_components/assistd/__init__.py new file mode 100644 index 0000000..1c0835e --- /dev/null +++ b/custom_components/assistd/__init__.py @@ -0,0 +1,64 @@ +"""assistd - exposes assist-exposed entities via REST.""" +from __future__ import annotations + +import logging +from typing import Any + +from homeassistant.components.http import HomeAssistantView +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.typing import ConfigType + +_LOGGER = logging.getLogger(__name__) + +DOMAIN = "assistd" + + +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up assistd.""" + hass.http.register_view(AssistdView(hass)) + _LOGGER.info("assistd registered at /api/assistd") + return True + + +class AssistdView(HomeAssistantView): + """View to return entities exposed to Assist.""" + + url = "/api/assistd" + name = "api:assistd" + requires_auth = True + + def __init__(self, hass: HomeAssistant) -> None: + """Initialize the view.""" + self._hass = hass + + async def get(self, request) -> dict[str, Any]: + """Return list of entities exposed to Assist/conversation.""" + registry = er.async_get(self._hass) + exposed = [] + + for entity_id, entry in registry.entities.items(): + # check if exposed to conversation/assist + options = entry.options or {} + conversation_opts = options.get("conversation", {}) + + if conversation_opts.get("should_expose"): + # get current state + state = self._hass.states.get(entity_id) + state_val = state.state if state else "unknown" + attrs = state.attributes if state else {} + + exposed.append({ + "entity_id": entity_id, + "name": entry.name or entry.original_name or attrs.get("friendly_name", entity_id), + "aliases": list(entry.aliases) if entry.aliases else [], + "domain": entry.domain, + "platform": entry.platform, + "state": state_val, + "area_id": entry.area_id, + }) + + return self.json({ + "count": len(exposed), + "entities": exposed + }) diff --git a/custom_components/assistd/manifest.json b/custom_components/assistd/manifest.json new file mode 100644 index 0000000..e11995e --- /dev/null +++ b/custom_components/assistd/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "assistd", + "name": "assistd", + "codeowners": ["@sudoxnym"], + "config_flow": false, + "documentation": "https://github.com/sudoxnym/ha-assistd", + "integration_type": "service", + "iot_class": "local_push", + "requirements": [], + "version": "1.0.0" +} diff --git a/hacs.json b/hacs.json new file mode 100644 index 0000000..eef868b --- /dev/null +++ b/hacs.json @@ -0,0 +1,5 @@ +{ + "name": "assistd", + "homeassistant": "2024.1.0", + "render_readme": true +}