AboveColin/HA-Jev

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

See the code
ai
custom-components
hacs
home-assistant
homeassistant
home-automation
jev
typesafe

README

Tests hassfest HACS Action hacs_badge GitHub release License

Jev for Home Assistant

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.

What it does

  • Questions in configuration.yaml become sensors: a probability, one of your options with its distribution, or a number that can land between levels.
  • Four actions answer inside an automation and return a response variable: jev.noul, jev.choice, jev.score and jev.ask.
  • Point a question at entities, devices, areas, floors or labels in the normal picker and the state is built for you, so no template is needed.
  • Reports what it spends: calls, input tokens and estimated cost per day, plus a daily token budget that halts evaluation when it trips.
  • Thirteen worked examples, four of them pairing Jev with an LLM.
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.

Installation

Requires Home Assistant 2026.9 or newer and an API key from typesafe.ai.

HACS

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.

  1. HACS, then the three dot menu, then Custom repositories.
  2. Paste https://github.com/AboveColin/HA-Jev, set Type to Integration, Add.
  3. Search HACS for Jev, then Download.
  4. Restart Home Assistant.

Open your Home Assistant instance and open a repository inside HACS.

Manual

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.

Configuration

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.

Open your Home Assistant instance and start setting up a new integration.

OptionWhereDefaultDescription
API keyconfig flownoneYour TypeSafe key
Daily input token budgetoptions0Stops evaluating for the day once spent. 0 means no limit
Price per million input tokensoptions0.042Only affects the estimated cost sensor

Use Reconfigure to replace the key later, which keeps your entities and history.

Actions

- 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. }
ActionYou give itYou get back
jev.noula yes/no questionnoul 0 to 1, is_true against your threshold
jev.choiceoptions:, 2 to 255choice, probabilities, confidence
jev.scorelevels:, 2 to 10, lowest firstscore, normalized, nearest_level, legend, probabilities, confidence
jev.askany mix, under your own keysthe 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.

Sensors

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]
KeyRequiredDescription
nameyesNames the context and prefixes its entities
entitiesone of these twoEntities, devices, areas, floors or labels to read
stateone of these twoText or a template, alone or as a note beside the entities
scan_intervalnoSeconds between evaluations, minimum 30, default 300
trigger_entitiesnoWake on these instead of on whatever entities names
include_attributesnoSend every attribute of the picked entities, off by default
questionsyesEach 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.

Examples

01 laundry reminderone question, one threshold, one binary sensor
02 alert triagethree questions in one call, three notification paths
03 doorbell triagea choice on an intercom transcript
04 situation layernamed situations other automations trigger on
05 confidence gatingact, ask, or stay quiet
06 composite scoreseveral scores combined with your own weights
07 Jev gates the LLMa cheap typed decision in front of an expensive call
08 cascadelow confidence escalates to a reasoning model
09 guardrailthe LLM writes, Jev checks it against the source
10 extract then verifythe LLM pulls fields, Jev verifies each one
11 post and parcelsone attention queue across several channels
12 energy windowwhere to keep arithmetic and where to ask
13 voice commandsa 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.

Measurements

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.

Known limitations

  • Answers carry no reasoning, so there is nothing to audit afterwards.
  • Confidence has no published calibration evidence. Treat 0.9 as higher than 0.6 until you have measured it on your own questions.
  • Slower from Europe than the published 70 to 500 ms. Fine for a doorbell, too slow for a tight loop.
  • Not for safety decisions. A probability with no explanation should not hold a lock, a heater or a smoke alarm.

Troubleshooting

Turn on debug logging first. It prints every state sent, which is usually the answer:

logger:
  logs:
    custom_components.jev: debug
SymptomCause
An answer barely moves with the worldThe 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 confidenceThe question measures more than one thing. Split it
Entities unavailable, budget sensor onThe daily budget stopped evaluation
Entities unavailable, budget sensor offLook 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 limitIt names your number too. 2 to 255 options, 2 to 10 levels, 250 entities

Contributing

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.

Changelog

See the release history.

Contributors

AboveColin

22 commits

AboveColin/HA-Jev

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

See the code
ai
custom-components
hacs
home-assistant
homeassistant
home-automation
jev
typesafe

README

Tests hassfest HACS Action hacs_badge GitHub release License

Jev for Home Assistant

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.

What it does

  • Questions in configuration.yaml become sensors: a probability, one of your options with its distribution, or a number that can land between levels.
  • Four actions answer inside an automation and return a response variable: jev.noul, jev.choice, jev.score and jev.ask.
  • Point a question at entities, devices, areas, floors or labels in the normal picker and the state is built for you, so no template is needed.
  • Reports what it spends: calls, input tokens and estimated cost per day, plus a daily token budget that halts evaluation when it trips.
  • Thirteen worked examples, four of them pairing Jev with an LLM.
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.

Installation

Requires Home Assistant 2026.9 or newer and an API key from typesafe.ai.

HACS

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.

  1. HACS, then the three dot menu, then Custom repositories.
  2. Paste https://github.com/AboveColin/HA-Jev, set Type to Integration, Add.
  3. Search HACS for Jev, then Download.
  4. Restart Home Assistant.

Open your Home Assistant instance and open a repository inside HACS.

Manual

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.

Configuration

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.

Open your Home Assistant instance and start setting up a new integration.

OptionWhereDefaultDescription
API keyconfig flownoneYour TypeSafe key
Daily input token budgetoptions0Stops evaluating for the day once spent. 0 means no limit
Price per million input tokensoptions0.042Only affects the estimated cost sensor

Use Reconfigure to replace the key later, which keeps your entities and history.

Actions

- 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. }
ActionYou give itYou get back
jev.noula yes/no questionnoul 0 to 1, is_true against your threshold
jev.choiceoptions:, 2 to 255choice, probabilities, confidence
jev.scorelevels:, 2 to 10, lowest firstscore, normalized, nearest_level, legend, probabilities, confidence
jev.askany mix, under your own keysthe 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.

Sensors

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]
KeyRequiredDescription
nameyesNames the context and prefixes its entities
entitiesone of these twoEntities, devices, areas, floors or labels to read
stateone of these twoText or a template, alone or as a note beside the entities
scan_intervalnoSeconds between evaluations, minimum 30, default 300
trigger_entitiesnoWake on these instead of on whatever entities names
include_attributesnoSend every attribute of the picked entities, off by default
questionsyesEach 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.

Examples

01 laundry reminderone question, one threshold, one binary sensor
02 alert triagethree questions in one call, three notification paths
03 doorbell triagea choice on an intercom transcript
04 situation layernamed situations other automations trigger on
05 confidence gatingact, ask, or stay quiet
06 composite scoreseveral scores combined with your own weights
07 Jev gates the LLMa cheap typed decision in front of an expensive call
08 cascadelow confidence escalates to a reasoning model
09 guardrailthe LLM writes, Jev checks it against the source
10 extract then verifythe LLM pulls fields, Jev verifies each one
11 post and parcelsone attention queue across several channels
12 energy windowwhere to keep arithmetic and where to ask
13 voice commandsa 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.

Measurements

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.

Known limitations

  • Answers carry no reasoning, so there is nothing to audit afterwards.
  • Confidence has no published calibration evidence. Treat 0.9 as higher than 0.6 until you have measured it on your own questions.
  • Slower from Europe than the published 70 to 500 ms. Fine for a doorbell, too slow for a tight loop.
  • Not for safety decisions. A probability with no explanation should not hold a lock, a heater or a smoke alarm.

Troubleshooting

Turn on debug logging first. It prints every state sent, which is usually the answer:

logger:
  logs:
    custom_components.jev: debug
SymptomCause
An answer barely moves with the worldThe 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 confidenceThe question measures more than one thing. Split it
Entities unavailable, budget sensor onThe daily budget stopped evaluation
Entities unavailable, budget sensor offLook 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 limitIt names your number too. 2 to 255 options, 2 to 10 levels, 250 entities

Contributing

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.

Changelog

See the release history.

See what people are saying

Contributors

AboveColin

22 commits

Languages

Python

100.0%