SystemOne is a Home Assistant integration for decision-model APIs that implement
the TypeSafe Jev POST /v1/systemone format.
It works with:
- hosted TypeSafe Jev;
- self-hosted SystemOne-compatible servers;
- custom model hosts, including R9V/Qwen proxies;
- authenticated and unauthenticated endpoints.
The integration sends a shared state plus one or more typed questions and receives probabilities, choices, or scores. It does not load a model inside Home Assistant.
- Native Home Assistant
AI Taskentity for structured decisions. - Boolean fields mapped to SystemOne
noulquestions. - Select/enum fields mapped to
choicequestions. - Bounded numeric fields mapped to ordinal
scorequestions. - Multiple AI Task fields evaluated in one
/v1/systemonerequest. - Configurable API base URL, optional bearer token, and model ID.
- Existing
jev.noul,jev.choice,jev.score, andjev.askactions. - YAML-defined decision sensors and binary sensors.
- Optional Assist conversation router.
- Usage, latency, estimated-cost, and daily-budget entities.
This project was originally HA-Jev. The Home Assistant domain remains jev so
existing entity IDs, service calls, YAML configuration, and automations continue to
work. The integration name and provider behavior are now generic SystemOne.
Home Assistant 2026.9 or newer is required.
Add this fork as a custom repository:
- Open HACS.
- Open the three-dot menu and choose Custom repositories.
- Enter
https://github.com/AtHeartEngineer/HA-SystemOne. - Select Integration and choose Add.
- Search for SystemOne, install it, and restart Home Assistant.
Download the latest release,
copy custom_components/jev into config/custom_components/, and restart Home
Assistant.
Go to Settings → Devices & services → Add integration → SystemOne.
| Setting | Default | Description |
|---|---|---|
| Base URL | https://api.typesafe.ai |
Root URL for the compatible API |
| API token | blank | Optional bearer token |
| Model | jev-latest |
Model identifier sent to the server |
The integration normalizes base URLs ending in /, /v1, or /v1/systemone and
always sends requests to the correct /v1/systemone endpoint. When the token is
blank, no Authorization header is sent.
Setup tries the optional GET /v1/models route without running inference. A server
without that route is still supported; the first real request validates the
SystemOne endpoint.
Base URL: https://api.typesafe.ai
API token: <your TypeSafe token>
Model: jev-latest
Base URL: http://192.168.1.50:8000
API token: <leave blank if authentication is disabled>
Model: Qwen/Qwen3.8-Flash-Next
The model can be any identifier accepted by the configured server. There is no separate self-hosted mode.
Each configured server creates ai_task.jev_ai_task. The legacy jev domain keeps
the integration compatible with existing installations; the entity ID can be
renamed normally.
Supported output fields are translated as follows:
| Home Assistant structure | SystemOne question | Result |
|---|---|---|
| Boolean | noul |
Python boolean using a 0.5 threshold |
| Select/enum | choice |
Exact configured option value |
| Bounded number | score |
Value mapped back into the requested range |
| Free text | unsupported | Clear error before any API request |
One structured task becomes one HTTP request. For example, the following three fields become three questions under one shared state:
action: ai_task.generate_data
data:
entity_id: ai_task.jev_ai_task
task_name: office_state
instructions: >-
Office presence sensor: on.
Desk computer power: 138 watts.
Ceiling lights: on.
Last motion: 12 seconds ago.
structure:
occupied:
description: Is someone currently in the office?
required: true
selector:
boolean:
activity:
description: What is the most likely state of the office?
required: true
selector:
select:
options:
- empty
- working
- relaxing
- uncertain
confidence_needed:
description: How strongly should an automation rely on this interpretation?
required: true
selector:
number:
min: 0
max: 5
step: 1
response_variable: resultThe result is ordinary Home Assistant structured data:
result.data.occupied
result.data.activity
result.data.confidence_neededExample value:
{
"occupied": true,
"activity": "working",
"confidence_needed": 4
}SystemOne probability and confidence metadata is kept out of the requested AI Task schema. Request counts, latency, and token usage are available in diagnostics.
The original typed actions remain available for automations that want direct access to SystemOne response metadata.
| Action | Question type | Main result |
|---|---|---|
jev.noul |
Yes/no probability | noul, plus thresholded is_true |
jev.choice |
One option from 2–255 choices | choice, probabilities, confidence |
jev.score |
Ordered scale of 2–10 levels | weighted score, legend, confidence |
jev.ask |
Several mixed questions | typed answers from one request |
action: jev.noul
data:
state: >-
Washing-machine power: {{ states('sensor.washing_machine_power') }} W
instructions: Is the washing cycle finished?
threshold: 0.7
response_variable: laundryRelated questions should use jev.ask so they share one request and one state.
Existing YAML configuration remains supported:
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 in the machine?
threshold: 0.7
- name: Reminder urgency
type: score
instructions: How urgently should someone be reminded?
criteria:
- Not at all
- When convenient
- Right nowEvery context batches its questions into one SystemOne request. See the
examples/ directory for more automation patterns.
The optional conversation entity routes supported smart-home commands through the configured SystemOne model and Home Assistant's intent system. It supports on, off, toggle, brightness, and state questions. Unsupported or low-confidence requests can be forwarded to a configured fallback conversation agent.
Only entities exposed to Assist are included. Whole-home commands are refused by default except for turning everything off.
Integration options include:
- daily input-token budget;
- price per million input tokens for estimated-cost sensors;
- fallback conversation agent;
- minimum conversation confidence;
- whole-home command permission.
Diagnostics include the endpoint URL, configured model, authentication presence, usage totals, and the last AI Task request statistics. API tokens and Authorization headers are always redacted. AI Task instructions and household state are not added to AI Task diagnostics.
- AI Task supports decision-oriented structured data only.
- Free-form text, images, attachments, and AI Task tool calling are not supported.
- A task containing any unsupported field is rejected before inference.
- Large or continuous numeric ranges use up to ten representative score levels and are mapped back into the requested range.
- Optional AI Task fields are currently answered rather than omitted.
- Probability and confidence calibration depends on the configured model host.
- Do not use model decisions as the sole control for safety-critical equipment.
| Problem | Check |
|---|---|
| Cannot connect | Base URL, DNS, port, and server availability |
| Authentication rejected | Token value and server authentication settings |
| TLS failure | Certificate validity and hostname |
| Incompatible response | Server implements the TypeSafe Jev /v1/systemone response format |
| Unknown choice | Server returned a value outside the requested select options |
| Unsupported AI Task field | Use boolean, select/enum, or bounded number selectors |
Debug logging can be enabled with:
logger:
logs:
custom_components.jev: debugNever publish logs or diagnostics without checking them for household information.
pip install -r requirements-test.txt
pytest
ruff check custom_components tests
ruff format --check custom_components tests
mypy --strict --ignore-missing-imports custom_components/jevThe project is validated with pytest, Ruff, strict mypy, hassfest, and the HACS validation action.