mirror of
https://github.com/sudoxnym/ha-assistd.git
synced 2026-04-14 11:37:20 +00:00
initial release - assistd REST API for assist-exposed entities
This commit is contained in:
commit
5ea269d952
5 changed files with 194 additions and 0 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -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.
|
||||||
93
README.md
Normal file
93
README.md
Normal file
|
|
@ -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
|
||||||
64
custom_components/assistd/__init__.py
Normal file
64
custom_components/assistd/__init__.py
Normal file
|
|
@ -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
|
||||||
|
})
|
||||||
11
custom_components/assistd/manifest.json
Normal file
11
custom_components/assistd/manifest.json
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
5
hacs.json
Normal file
5
hacs.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "assistd",
|
||||||
|
"homeassistant": "2024.1.0",
|
||||||
|
"render_readme": true
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue