mirror of
https://github.com/sudoxnym/ha-assistd.git
synced 2026-04-14 03:27:02 +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