Skip to main content
Local LLMs

How to Connect Jan to Home Assistant: Local LLM Integration

· · 6 min read

Running Jan on your homelab means you have a local LLM with an API. Home Assistant has automation rules. The obvious next move is to connect Jan to Home Assistant so your smart home can actually understand natural language. It sounds cleaner in theory than it works in practice, but if you know the gaps, it’s genuinely useful.

๐ŸŽฏ Not sure if this will run on your hardware?Use our free Local LLM Hardware Checker โ€” pick your GPU and RAM, see which models will run with real tokens/sec estimates.
Check my hardware โ†’
Jan screenshot
Jan u2014 from the official site

Why Connect Jan to Home Assistant at All

Home Assistant’s built-in AI stuff either routes through the cloud or relies on older, smaller models. Jan runs whatever you want locally, and it exposes an OpenAI-compatible API on port 1337 by default. That’s the lever you need.

The real upside: voice intents that understand context. Instead of “turn on the lights in the bedroom,” you can feed natural language directly to Jan, parse the response, and trigger actual automations. Or use it to generate summaries of your home’s activity. Or just have a chatbot that knows your device names without cloud calls.

The caveat: Home Assistant doesn’t have native Jan support. You’re gluing it together with REST calls and templates. That’s not a dealbreaker, but it means more configuration and more things that can silently fail.

Prerequisites and Hardware

You need Jan and Home Assistant running on the same network. Jan should be on a stable machine with at least 8GB of RAM; 16GB is safer if you’re also running Home Assistant on the same box. Both can coexist fine on a single moderate-spec Intel NUC or comparable.

Jan must be exposed on your local network. If you’re only running it on localhost, Home Assistant can’t reach it. For this setup, I’m assuming both are on the same LAN with fixed IPs or hostnames.

You’ll need a model loaded in Jan. I’ve tested this with Mistral 7B and Llama 2 13B. Smaller models are faster but less accurate at understanding intent. Start with a 7B model if you’re unsure what to use.

Step 1: Expose Jan’s API Properly

Out of the box, Jan’s API server listens on 127.0.0.1:1337. Home Assistant can’t reach it from another machine. You need to change the bind address.

Jan’s config lives in different places depending on your OS. On Linux, it’s typically ~/.jan/settings.json. On Mac, it’s in ~/Library/Application Support/Jan. Open the settings file and look for the API section.

You should see something like this (the file structure varies slightly by version, but the pattern is consistent):

{
  "api": {
    "host": "127.0.0.1",
    "port": 1337,
    "corsEnabled": false
  },
  "models": [
    // model list here
  ]
}

Change "host": "127.0.0.1" to "host": "0.0.0.0". This makes Jan listen on all network interfaces. Also set "corsEnabled": true because Home Assistant will be making cross-origin requests.

Save and restart Jan. Verify the API is accessible from another machine on your network by testing a simple curl:

curl http://[jan-machine-ip]:1337/v1/models

You should get back a JSON list of loaded models. If you get a connection refused or timeout, Jan’s API isn’t exposed yet. Check that Jan is actually running and the settings changes took effect.

Step 2: Configure Home Assistant to Call Jan

Home Assistant needs a way to send requests to Jan and parse responses. The cleanest approach is using the rest integration combined with a template sensor and an automation.

In your Home Assistant configuration.yaml, add a REST sensor that calls Jan’s chat completion endpoint:

rest:
  - scan_interval: 3600
    resource: http://[jan-machine-ip]:1337/v1/chat/completions
    method: POST
    name: Jan LLM
    headers:
      Content-Type: application/json
    payload: |
      {
        "model": "mistral",
        "messages": [{
          "role": "user",
          "content": "{{ state_attr('input_text.llm_prompt', 'value') }}"
        }],
        "temperature": 0.7,
        "max_tokens": 500
      }
    value_template: "{{ value_json.choices[0].message.content }}"
    json_attributes:
      - usage

This assumes you create an input_text helper called llm_prompt where you feed the prompt. The response gets stored in the sensor state.

Restart Home Assistant after adding this. Check the developer tools under States to confirm the sensor appears.

Step 3: Wire It Into an Automation

Raw REST sensors are passive. You need an automation to trigger them and act on the response.

Here’s an example that listens for voice input via Home Assistant’s conversation integration, sends it to Jan, and logs the response:

automation:
  - alias: Query Jan LLM
    trigger:
      platform: state
      entity_id: input_text.llm_prompt
    action:
      - service: rest.get_template_sensor
        target:
          entity_id: sensor.jan_llm
      - service: logger.write
        data:
          message: "Jan response: {{ state_attr('sensor.jan_llm', 'response') }}"
      - service: tts.google_translate_say
        data:
          entity_id: media_player.living_room
          message: "{{ states('sensor.jan_llm') }}"

This is a basic flow. Real usage gets more complex because you’ll want to parse intent from Jan’s response and trigger specific automations. You might add a template to extract device names or actions from the LLM output, then use that to call the right light.turn_on or climate.set_temperature service.

Where This Gets Messy

The honest part: Jan’s responses aren’t always structured. If you ask it to turn on the lights, it might respond with “I’ll turn on the lights” or “Turning on the lights now” or “Lights are on” depending on the model and temperature setting. Parsing intent from free-form text is doable but adds another layer of complexity.

One workaround is to prompt Jan with a specific format. Ask it to respond with JSON:

"content": "The user said: {{ prompt }}. Respond ONLY with valid JSON in this format: {"action": "turn_on", "device": "bedroom_lights", "confidence": 0.95}"

Models are reasonably good at this, but not perfect. Mistral 7B follows this instruction about 90% of the time in my testing. Llama 2 was closer to 75%. The remaining failures require error handling.

Also, Jan’s API can be slow. A Mistral 7B inference takes 3-5 seconds on a modest GPU, longer on CPU. If you’re using this for real-time voice control, expect noticeable latency. It’s fine for batch summaries or offline analysis. It’s frustrating for voice intents.

Testing the Integration

Start with a simple prompt. Open Developer Tools in Home Assistant, go to the States tab, and manually set the input_text.llm_prompt to something like “What is the current time?”

Wait 5-10 seconds for Jan to respond. Check the sensor.jan_llm state. If it populated with an answer, the connection works.

Then test something that requires intent parsing: “Turn on the bedroom lights.” Store Jan’s response. If it says “I’ll turn on the bedroom lights” or something similar, your automation can extract the intent. If it says “I don’t know what bedroom lights are,” your model might be too small or poorly prompted.

This is where you discover whether your setup is actually useful or just technically working. The testing phase takes longer than the configuration phase, honestly.

Security Note

Exposing Jan’s API on 0.0.0.0 on your LAN is fine. Anyone on your network can query it, which might matter if you share the network or have guests. For a truly locked-down setup, use a firewall rule or keep it on a VLAN.

Also note that Home Assistant’s REST sensor will expose your Jan API URL in its state if you log it. Nothing catastrophic, but worth knowing.

FAQ

Can Jan run on the same machine as Home Assistant?

Yes. If Home Assistant is containerized, expose Jan’s API on the Docker host’s IP. If both run natively, Jan can bind to 0.0.0.0:1337 and Home Assistant calls it directly. RAM usage is the constraint, not architecture.

What if Jan crashes or becomes unresponsive?

The REST sensor call will timeout after 10 seconds by default. Home Assistant logs the error and moves on. Add a timeout check in your automation to avoid getting stuck. You can also use a template condition to verify the sensor state before triggering voice output.

Can I use Jan with Home Assistant’s conversation integration?

Not directly. Home Assistant’s native conversation system expects specific response formats that Jan doesn’t always follow. The REST sensor approach is more reliable because you control the prompt structure.

How much slower is this than cloud-based AI?

Mistral 7B takes 3-5 seconds per request on a 4GB VRAM GPU, longer on CPU. Cloud APIs respond in under a second usually. For voice intents, the difference is noticeable. For batch operations like generating a summary of the day’s activity, it doesn’t matter.

Does Jan need a GPU to work with Home Assistant?

No, but CPU inference is slow. A model that takes 5 seconds on GPU takes 45 seconds on CPU. If you’re testing or don’t care about latency, CPU is fine. For real use, a GPU helps a lot.

Explore Jan in our AI Homelab Toolkit.

Share this article