📝 Tutorials
· 6 min read

Automate Your Home with Ollama + Home Assistant (2026)


What if you could type “turn off all lights at 11pm” and get a working Home Assistant automation back — no cloud AI, no subscription, everything running on your own hardware? That’s exactly what we’re building today.

We’ll wire up Ollama as a natural language interface for Home Assistant. You describe what you want in plain English, a local LLM translates it into automation YAML, and a Python script pushes it straight into Home Assistant via its REST API. The entire pipeline stays on your network. If you care about privacy and GDPR compliance, this is the way to do it.

What you need

  • Home Assistant (2024.x or later) with a Long-Lived Access Token
  • Ollama running locally with a model like llama3 or mistral pulled
  • Python 3.10+ with requests and pyyaml
  • A machine that can run both — see our hardware recommendations if you’re shopping for gear

If you’ve already set up Ollama for voice control, you’re halfway there. This tutorial focuses on the automation-generation side rather than real-time voice commands.

Architecture overview

The flow is simple:

User prompt → Ollama (local LLM) → Automation YAML → HA REST API → Live automation
  1. You describe an automation in natural language.
  2. A Python script sends that prompt to Ollama with a system message that constrains output to valid HA automation YAML.
  3. The script parses the YAML response and validates it.
  4. It pushes the automation to Home Assistant using the REST API.

No cloud round-trips. No API keys from OpenAI or Google. Just your CPU (or GPU) doing the work.

Step 1: Configure Ollama

Make sure Ollama is running and you have a model available:

ollama pull llama3
ollama serve

By default Ollama listens on http://localhost:11434. Verify it’s up:

curl http://localhost:11434/api/tags

Step 2: Get a Home Assistant access token

In Home Assistant, go to Profile → Long-Lived Access Tokens → Create Token. Copy it — you’ll need it for the script.

Your HA instance should be reachable at something like http://homeassistant.local:8123.

Step 3: The bridge script

Here’s the Python script that ties everything together. Save it as ha_automate.py:

#!/usr/bin/env python3
"""Bridge between Ollama and Home Assistant — natural language to automation YAML."""

import json
import sys
import requests
import yaml

OLLAMA_URL = "http://localhost:11434/api/generate"
OLLAMA_MODEL = "llama3"

HA_URL = "http://homeassistant.local:8123"
HA_TOKEN = "YOUR_LONG_LIVED_ACCESS_TOKEN"

SYSTEM_PROMPT = """You are a Home Assistant automation generator.
The user will describe an automation in plain English.
Respond with ONLY valid Home Assistant automation YAML — no explanation, no markdown fences.
Use the standard schema: alias, description, trigger, condition (if needed), action.
Use platform types like 'time', 'state', 'numeric_state', 'sun' for triggers.
Use services like 'light.turn_off', 'switch.turn_on', 'climate.set_temperature', etc.
If the user mentions 'all lights', use entity_id: all for the light domain."""


def ask_ollama(prompt: str) -> str:
    """Send a prompt to Ollama and return the raw text response."""
    resp = requests.post(OLLAMA_URL, json={
        "model": OLLAMA_MODEL,
        "system": SYSTEM_PROMPT,
        "prompt": prompt,
        "stream": False,
    })
    resp.raise_for_status()
    return resp.json()["response"]


def parse_automation_yaml(text: str) -> dict:
    """Parse and validate the YAML returned by the LLM."""
    # Strip markdown fences if the model adds them anyway
    text = text.strip().removeprefix("```yaml").removeprefix("```").removesuffix("```").strip()
    automation = yaml.safe_load(text)
    # Basic validation
    required = {"alias", "trigger", "action"}
    if not isinstance(automation, dict) or not required.issubset(automation.keys()):
        raise ValueError(f"Missing required keys. Got: {list(automation.keys())}")
    return automation


def push_to_ha(automation: dict) -> dict:
    """Create the automation in Home Assistant via the REST API."""
    headers = {
        "Authorization": f"Bearer {HA_TOKEN}",
        "Content-Type": "application/json",
    }
    # Use the config/automation/config endpoint
    payload = {
        "alias": automation["alias"],
        "description": automation.get("description", ""),
        "trigger": automation["trigger"],
        "condition": automation.get("condition", []),
        "action": automation["action"],
        "mode": automation.get("mode", "single"),
    }
    resp = requests.post(
        f"{HA_URL}/api/config/automation/config/new",
        headers=headers,
        json=payload,
    )
    resp.raise_for_status()
    return resp.json()


def main():
    if len(sys.argv) < 2:
        print("Usage: python ha_automate.py 'turn off all lights at 11pm'")
        sys.exit(1)

    prompt = " ".join(sys.argv[1:])
    print(f"🧠 Asking Ollama: {prompt}")

    raw_yaml = ask_ollama(prompt)
    print(f"\n📄 Generated YAML:\n{raw_yaml}\n")

    automation = parse_automation_yaml(raw_yaml)
    print(f"✅ Parsed automation: {automation['alias']}")

    confirm = input("Push to Home Assistant? [y/N] ")
    if confirm.lower() == "y":
        result = push_to_ha(automation)
        print(f"🏠 Automation created: {result}")
    else:
        # Save locally instead
        with open("automation_output.yaml", "w") as f:
            yaml.dump(automation, f, default_flow_style=False)
        print("💾 Saved to automation_output.yaml")


if __name__ == "__main__":
    main()

Install the dependencies:

pip install requests pyyaml

Run it:

python ha_automate.py "turn off all lights at 11pm"

You’ll see something like this come back from the LLM:

alias: Turn off all lights at 11pm
description: Automatically turns off every light at 23:00
trigger:
  - platform: time
    at: "23:00:00"
condition: []
action:
  - service: light.turn_off
    target:
      entity_id: all
mode: single

The script validates it, shows you the YAML, and asks for confirmation before pushing it to Home Assistant. If you decline, it saves the file locally so you can review or paste it manually.

Step 4: Custom sentences and intents

Home Assistant supports custom sentences that map natural language to intents. You can combine this with Ollama to handle more complex requests that go beyond simple automation creation.

Create a custom sentences file at config/custom_sentences/en/automations.yaml:

language: en
intents:
  CreateAutomation:
    data:
      - sentences:
          - "create automation {description}"
          - "automate {description}"
          - "set up automation for {description}"

Then register the intent in your configuration.yaml:

intent_script:
  CreateAutomation:
    speech:
      text: "Creating automation: {{ description }}"
    action:
      - service: rest_command.ollama_automate
        data:
          prompt: "{{ description }}"

And add the REST command:

rest_command:
  ollama_automate:
    url: "http://localhost:5000/generate"
    method: POST
    content_type: "application/json"
    payload: '{"prompt": "{{ prompt }}"}'

This assumes you wrap the Python script in a small Flask or FastAPI server (just add an endpoint that calls ask_ollama and push_to_ha). Now you can trigger automation creation from HA’s conversation interface or even through voice commands.

Tips for better results

Be specific with your prompts. “Turn off the living room lights when no motion is detected for 10 minutes” gives much better YAML than “lights off when nobody’s around.”

Pick the right model. llama3 and mistral both handle structured YAML output well. Smaller models like phi3 work but occasionally produce invalid syntax. If you have the VRAM, llama3:70b is noticeably more reliable.

Add entity context. You can improve accuracy by fetching your actual entity list from HA and injecting it into the system prompt:

def get_entities() -> list[str]:
    resp = requests.get(
        f"{HA_URL}/api/states",
        headers={"Authorization": f"Bearer {HA_TOKEN}"},
    )
    return [e["entity_id"] for e in resp.json()]

Append the entity list to SYSTEM_PROMPT so the model knows your real device names instead of guessing.

Validate before pushing. The script does basic key checking, but you could add schema validation with voluptuous or the HA automation schema directly for production use.

What you can build from here

This is a starting point. Some ideas to extend it:

  • Batch mode: feed a text file of automation descriptions and generate them all at once.
  • Edit mode: describe changes to existing automations (“make the porch light automation also trigger at sunset”).
  • Dashboard widget: embed a text input card in Lovelace that calls your bridge API.
  • Voice loop: combine with Ollama voice control for a fully local, spoken automation builder.

The key advantage over cloud-based solutions is control. Your automation descriptions, your device names, your routines — none of it leaves your network. You get the convenience of natural language with the privacy of self-hosted AI.

If you’re running into hardware limits, check our guide on the best hardware for local AI in smart homes. A machine with 16 GB of RAM and a mid-range GPU handles this workflow comfortably.

That’s it — a fully local, natural language automation pipeline for Home Assistant. No subscriptions, no cloud dependencies, just your words turned into working automations.