Home Assistant integration for TypeSafe Jev. Ask a question about your house and get a probability, a choice or a score as an entity.
Python
4
22 commits
updated Sep 17, 2026
Ask TypeSafe Jev questions about your house and get numbers back. Jev is a decision model rather than a chat model, so it answers a typed question with a probability, a choice or a score, and this integration turns each answer into an entity you can automate on.
Not affiliated with TypeSafe. The API client is jevclient.
configuration.yaml become sensors: a probability, one of your
options with its distribution, or a number that can land between levels.jev.noul, jev.choice, jev.score and jev.ask.automation:
- alias: Remind about the washing
triggers:
- trigger: state
entity_id: binary_sensor.jev_laundry_forgotten
to: "on"
for: "00:10:00"
actions:
- action: notify.mobile_app
data:
message: The washing is done and still in the machine.
Requires Home Assistant 2026.9 or newer and an API key from typesafe.ai.
Not in the HACS default list yet, so add it as a custom repository once. hacs/default#11052 is queued; when it merges, steps 1 and 2 go away.
https://github.com/AboveColin/HA-Jev, set Type to Integration, Add.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.
Settings, Devices and services, Add integration, then Jev (TypeSafe). The API key is the only thing it asks for, and it is checked before the entry is created.
| Option | Where | Default | Description |
|---|---|---|---|
| API key | config flow | none | Your TypeSafe key |
| Daily input token budget | options | 0 | Stops evaluating for the day once spent. 0 means no limit |
| Price per million input tokens | options | 0.042 | Only affects the estimated cost sensor |
Use Reconfigure to replace the key later, which keeps your entities and history.
- action: jev.noul
response_variable: laundry
target:
entity_id: sensor.washing_machine_power
data:
instructions: Is the laundry finished but still sitting in the machine?
background: >-
This machine draws under 5 W when idle and over 300 W while a programme runs.
threshold: 0.7
- if: "{{ laundry.is_true }}"
then:
- action: notify.mobile_app
data: { message: The washing is done and still in the machine. }
| Action | You give it | You get back |
|---|---|---|
jev.noul | a yes/no question | noul 0 to 1, is_true against your threshold |
jev.choice | options:, 2 to 255 | choice, probabilities, confidence |
jev.score | levels:, 2 to 10, lowest first | score, normalized, nearest_level, legend, probabilities, confidence |
jev.ask | any mix, under your own keys | the same, under answers |
All four take a template in state, or an object, or a list. They also take
background: for standing facts about how to read the state, which is
worth more attached to the question than to the state.
jev:
- name: Laundry
scan_interval: 300
entities:
- sensor.washing_machine_power
- binary_sensor.laundry_door
questions:
- name: Laundry forgotten
type: noul
instructions: Is the laundry finished but still sitting in the machine?
background: >-
This machine draws under 5 W when idle and over 300 W while a programme runs.
threshold: 0.7
- name: Nudge urgency
type: score
instructions: How urgently should someone be reminded?
criteria: [Not at all, When convenient, Right now]
| Key | Required | Description |
|---|---|---|
name | yes | Names the context and prefixes its entities |
entities | one of these two | Entities, devices, areas, floors or labels to read |
state | one of these two | Text or a template, alone or as a note beside the entities |
scan_interval | no | Seconds between evaluations, minimum 30, default 300 |
trigger_entities | no | Wake on these instead of on whatever entities names |
include_attributes | no | Send every attribute of the picked entities, off by default |
questions | yes | Each with name, type, instructions, and criteria for choice and score |
A context is one request, so keep related questions together. It is evaluated on
scan_interval, or when an entity it watches changes, debounced by 5 seconds. Adding
threshold: to a noul also creates a binary sensor to trigger on.
| 01 laundry reminder | one question, one threshold, one binary sensor |
| 02 alert triage | three questions in one call, three notification paths |
| 03 doorbell triage | a choice on an intercom transcript |
| 04 situation layer | named situations other automations trigger on |
| 05 confidence gating | act, ask, or stay quiet |
| 06 composite score | several scores combined with your own weights |
| 07 Jev gates the LLM | a cheap typed decision in front of an expensive call |
| 08 cascade | low confidence escalates to a reasoning model |
| 09 guardrail | the LLM writes, Jev checks it against the source |
| 10 extract then verify | the LLM pulls fields, Jev verifies each one |
| 11 post and parcels | one attention queue across several channels |
| 12 energy window | where to keep arithmetic and where to ask |
| 13 voice commands | a command router, 12 questions per request |
The LLM examples use ai_task.generate_data, so they work with Google Generative AI,
OpenAI, Anthropic or a local Ollama. The voice command router follows TypeSafe's own
smart home demo and builds its device
options from your entity registry, so the answer is an entity_id you can act on.
docs/measurements.md has what was measured against the live API: what an entity costs in tokens, why batching is nearly free, real latency from Europe against the published figure, and the two findings that changed this code.
Turn on debug logging first. It prints every state sent, which is usually the answer:
logger:
logs:
custom_components.jev: debug
| Symptom | Cause |
|---|---|
| An answer barely moves with the world | The state does not say what you assumed, or it holds a number the model is being asked to compare |
| Answers sit near 0.5 with low confidence | The question measures more than one thing. Split it |
| Entities unavailable, budget sensor on | The daily budget stopped evaluation |
| Entities unavailable, budget sensor off | Look for one line saying TypeSafe is not answering |
| Setup fails with "TypeSafe did not answer" | Connectivity, not configuration. Home Assistant retries |
| An error names a limit | It names your number too. 2 to 255 options, 2 to 10 levels, 250 entities |
Issues and pull requests welcome.
pip install -r requirements-test.txt
pytest
103 tests run the integration inside a real Home Assistant with the API client
replaced, so the suite spends nothing. quality_scale.yaml tracks this against Home
Assistant's quality scale, and mypy --strict runs in CI.
See the release history.
22 commits
Hacker News (1)
Python
100.0%
Home Assistant integration for TypeSafe Jev. Ask a question about your house and get a probability, a choice or a score as an entity.
Python
4
22 commits
updated Sep 17, 2026
Ask TypeSafe Jev questions about your house and get numbers back. Jev is a decision model rather than a chat model, so it answers a typed question with a probability, a choice or a score, and this integration turns each answer into an entity you can automate on.
Not affiliated with TypeSafe. The API client is jevclient.
configuration.yaml become sensors: a probability, one of your
options with its distribution, or a number that can land between levels.jev.noul, jev.choice, jev.score and jev.ask.automation:
- alias: Remind about the washing
triggers:
- trigger: state
entity_id: binary_sensor.jev_laundry_forgotten
to: "on"
for: "00:10:00"
actions:
- action: notify.mobile_app
data:
message: The washing is done and still in the machine.
Requires Home Assistant 2026.9 or newer and an API key from typesafe.ai.
Not in the HACS default list yet, so add it as a custom repository once. hacs/default#11052 is queued; when it merges, steps 1 and 2 go away.
https://github.com/AboveColin/HA-Jev, set Type to Integration, Add.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.
Settings, Devices and services, Add integration, then Jev (TypeSafe). The API key is the only thing it asks for, and it is checked before the entry is created.
| Option | Where | Default | Description |
|---|---|---|---|
| API key | config flow | none | Your TypeSafe key |
| Daily input token budget | options | 0 | Stops evaluating for the day once spent. 0 means no limit |
| Price per million input tokens | options | 0.042 | Only affects the estimated cost sensor |
Use Reconfigure to replace the key later, which keeps your entities and history.
- action: jev.noul
response_variable: laundry
target:
entity_id: sensor.washing_machine_power
data:
instructions: Is the laundry finished but still sitting in the machine?
background: >-
This machine draws under 5 W when idle and over 300 W while a programme runs.
threshold: 0.7
- if: "{{ laundry.is_true }}"
then:
- action: notify.mobile_app
data: { message: The washing is done and still in the machine. }
| Action | You give it | You get back |
|---|---|---|
jev.noul | a yes/no question | noul 0 to 1, is_true against your threshold |
jev.choice | options:, 2 to 255 | choice, probabilities, confidence |
jev.score | levels:, 2 to 10, lowest first | score, normalized, nearest_level, legend, probabilities, confidence |
jev.ask | any mix, under your own keys | the same, under answers |
All four take a template in state, or an object, or a list. They also take
background: for standing facts about how to read the state, which is
worth more attached to the question than to the state.
jev:
- name: Laundry
scan_interval: 300
entities:
- sensor.washing_machine_power
- binary_sensor.laundry_door
questions:
- name: Laundry forgotten
type: noul
instructions: Is the laundry finished but still sitting in the machine?
background: >-
This machine draws under 5 W when idle and over 300 W while a programme runs.
threshold: 0.7
- name: Nudge urgency
type: score
instructions: How urgently should someone be reminded?
criteria: [Not at all, When convenient, Right now]
| Key | Required | Description |
|---|---|---|
name | yes | Names the context and prefixes its entities |
entities | one of these two | Entities, devices, areas, floors or labels to read |
state | one of these two | Text or a template, alone or as a note beside the entities |
scan_interval | no | Seconds between evaluations, minimum 30, default 300 |
trigger_entities | no | Wake on these instead of on whatever entities names |
include_attributes | no | Send every attribute of the picked entities, off by default |
questions | yes | Each with name, type, instructions, and criteria for choice and score |
A context is one request, so keep related questions together. It is evaluated on
scan_interval, or when an entity it watches changes, debounced by 5 seconds. Adding
threshold: to a noul also creates a binary sensor to trigger on.
| 01 laundry reminder | one question, one threshold, one binary sensor |
| 02 alert triage | three questions in one call, three notification paths |
| 03 doorbell triage | a choice on an intercom transcript |
| 04 situation layer | named situations other automations trigger on |
| 05 confidence gating | act, ask, or stay quiet |
| 06 composite score | several scores combined with your own weights |
| 07 Jev gates the LLM | a cheap typed decision in front of an expensive call |
| 08 cascade | low confidence escalates to a reasoning model |
| 09 guardrail | the LLM writes, Jev checks it against the source |
| 10 extract then verify | the LLM pulls fields, Jev verifies each one |
| 11 post and parcels | one attention queue across several channels |
| 12 energy window | where to keep arithmetic and where to ask |
| 13 voice commands | a command router, 12 questions per request |
The LLM examples use ai_task.generate_data, so they work with Google Generative AI,
OpenAI, Anthropic or a local Ollama. The voice command router follows TypeSafe's own
smart home demo and builds its device
options from your entity registry, so the answer is an entity_id you can act on.
docs/measurements.md has what was measured against the live API: what an entity costs in tokens, why batching is nearly free, real latency from Europe against the published figure, and the two findings that changed this code.
Turn on debug logging first. It prints every state sent, which is usually the answer:
logger:
logs:
custom_components.jev: debug
| Symptom | Cause |
|---|---|
| An answer barely moves with the world | The state does not say what you assumed, or it holds a number the model is being asked to compare |
| Answers sit near 0.5 with low confidence | The question measures more than one thing. Split it |
| Entities unavailable, budget sensor on | The daily budget stopped evaluation |
| Entities unavailable, budget sensor off | Look for one line saying TypeSafe is not answering |
| Setup fails with "TypeSafe did not answer" | Connectivity, not configuration. Home Assistant retries |
| An error names a limit | It names your number too. 2 to 255 options, 2 to 10 levels, 250 entities |
Issues and pull requests welcome.
pip install -r requirements-test.txt
pytest
103 tests run the integration inside a real Home Assistant with the API client
replaced, so the suite spends nothing. quality_scale.yaml tracks this against Home
Assistant's quality scale, and mypy --strict runs in CI.
See the release history.
Hacker News (1)
22 commits
Python
100.0%