Install¶
Needs Home Assistant 2026.9 or newer and an API key from typesafe.ai. An endpoint of your own that needs no key can be set up without one.
Through HACS¶
Jev is in the HACS default list, so there is no custom repository to add.
- Open HACS
- Search for Jev, then Download
- Restart Home Assistant
By hand¶
Copy custom_components/jev from the
latest release into your
config/custom_components/ directory and restart.
HACS will not update a copy installed this way
A manual install is invisible to HACS, so you will not be told when a release lands. Prefer HACS unless you have a reason not to.
Setting it up¶
Settings, Devices and services, Add integration, then Jev (TypeSafe).
It asks for a provider and an API key.
| Provider | Address | Model | Price per million input tokens |
|---|---|---|---|
| Jev (TypeSafe) | https://api.typesafe.ai |
jev-latest |
$0.042 |
| SubQ Snap | https://api.subq.ai |
subq-snap-preview |
$0.09 |
| OpenRouter | https://openrouter.ai/api |
~typesafe/jev-latest |
TypeSafe's, until you set OpenRouter's |
| Local server | the one you give | jev-latest |
$0 |
Advanced is collapsed and holds the address and the model. Leave both empty to use the provider's own; a local server needs its address there. All of them are checked against a live request before the entry is created, so a wrong one fails here rather than silently later.
The key may be left empty only for a local server that asks for none. No
Authorization header is then sent at all. An empty key for a hosted provider is
refused without spending a request, because those APIs answer 401 to every request
without a key.
The price in the table is where the cost sensor starts. Change it in the options if your provider bills you something else.
SubQ keeps request data
SubQ's default data retention is to keep requests indefinitely. The Decisions API has no per-request opt-out. Ask SubQ for zero data retention before you send it the state of your home.
An entry is identified by its address and its key together, hashed. Two entries cannot hold the same key at the same address. The same key at two addresses is allowed, and is two entries with two daily budgets, so that key can spend twice what one budget allows. Two keyless endpoints are two entries as long as the addresses differ.
Use Reconfigure to change the provider, the key, the address or the model later. That keeps your entities and their history.
A different API address¶
The address defaults to https://api.typesafe.ai and most people should leave it
there. Change it when you run something of your own that speaks the same API: a
proxy that holds the key once for several clients, caches answers, or meters what is
spent across more than Home Assistant.
Whatever is behind it has to answer POST /v1/systemone the way TypeSafe does. A
path is kept as a prefix, so http://gateway.local:8080/jev is asked at
http://gateway.local:8080/jev/v1/systemone. A query, a fragment, a space, or a username
or password in the address are all refused: the first two cannot survive having that
path appended, a space is a typo rather than a host, and the last would be written
into a diagnostics file, which redacts secrets by name and would not recognise
those. Clearing the field goes back to
TypeSafe.
Give the address without the request path
/v1/systemone is added for you. An address that already carries it, or one
copied out of an API reference, ends up asking for /v1/systemone/v1/systemone
and the setup fails with "The server answered HTTP 404". That is a host that
answered, not a host that could not be reached, and the form says so separately.
Through OpenRouter¶
OpenRouter resells the model. Pick OpenRouter as the provider and paste an OpenRouter key; the address and the model id come with it.
Your OpenRouter spend is not visible from here. The cost sensor multiplies the tokens the endpoint reports by the price you set in the options, so put OpenRouter's price there rather than TypeSafe's, or read the cost as tokens only.
A different model¶
The model defaults to jev-latest, which is what TypeSafe serves. Change it when
your endpoint publishes its own names. The id is sent in the request body, so what
counts is what that endpoint accepts. Only a space is refused locally. An id the
endpoint rejects comes back as "That model id was not accepted", with the form still
open. Clearing the field goes back to jev-latest.
An http address sends the key in clear
The key travels as a bearer header. Over http anything that can see that
traffic can read it. Home Assistant writes one warning per setup saying so,
unless the address is loopback. Prefer https, or keep the endpoint on a network
you trust.
Options¶
| Option | Default | What it does |
|---|---|---|
| API address | https://api.typesafe.ai |
Where requests go. In the config flow, not the options |
| Model | jev-latest |
Which model the endpoint is asked for. In the config flow, not the options |
| Daily input token budget | 0 | Stops evaluating for the day once spent. 0 means no limit |
| Price per million input tokens | 0.042 | Only affects the estimated cost sensor |
| Fall back to this agent | none | Where the conversation agent sends what it cannot route |
| Act only above this confidence | 0.6 | Below it, a spoken command goes to the fallback |
| Allow whole-house commands | off | Commands naming no room and no device. Turning everything off is always allowed |
The budget is a tripwire, not a quota to run against. Put it past anything a working setup would use.
Languages¶
The setup flow, the options, the question editor, the actions, the entity names, the repair issues and everything the conversation agent says back are translated into thirteen languages: English, Nederlands, Deutsch, Français, Italiano, Español, Português (Brasil), Polski, Svenska, Dansk, Čeština, Русский and 简体中文.
Home Assistant falls back to English key by key, so a missing string never shows a blank. The user interface follows your profile language. The conversation agent follows the language of the Assist pipeline that called it, which is not always the same one, and it accepts every language Assist offers.
Two of the agent's own lines reach further than those thirteen. "Sorry, I did not
understand that" and "Sorry, that did not work" are worded by Home Assistant in all
63 languages home-assistant-intents 2026.8.28 carries, so a Japanese pipeline
hears a Japanese sentence. Where this integration has its own translation, that one
is used.
The answer to a state question is not on the list either, and does not need to be. The sentence comes from the template Home Assistant's own Assist ships for that language, and the state word inside it comes from the state names Home Assistant already shows on a badge, so Italian answers "Luce Tavolo è spento" rather than "è off". The word follows the device class, which is what makes a door "aperto" and a motion sensor "rilevato" instead of both being "acceso". A number keeps its value and its unit, and German says "21,5 Grad" rather than "21.5 °C".
The measurements, against home-assistant-intents 2026.8.28 and Home Assistant
2026.8: 63 languages ship, 47 carry the state template. Three of those 47, Polish,
Russian and Thai, write the state word themselves by comparing against the English
one, so those three are handed the raw state and keep working. Of the remaining 44,
33 have Home Assistant's own word for the state. The other 11 keep the English word
inside a translated sentence, and the 16 languages with no template at all get an
English sentence.
Your own questions are not translated, because you wrote them. A choice comes back as one of the options you named, in whatever language you named them.
A correction or a language that is not here is welcome as a pull request. One file
under custom_components/jev/translations/, same keys as strings.json.