From 5054338b2e2a08cd66c0ff9a273492217f3f674f Mon Sep 17 00:00:00 2001 From: AboveColin Date: Wed, 23 Sep 2026 22:23:08 +0200 Subject: [PATCH 1/5] The docs cover calibration, the LLM tools, the ask-back, a month of cost and the Bayesian sensor --- mkdocs.yml | 10 ++++- site-docs/actions.md | 6 +++ site-docs/bayesian.md | 73 +++++++++++++++++++++++++++++++++++ site-docs/calibrate.md | 80 +++++++++++++++++++++++++++++++++++++++ site-docs/conversation.md | 23 ++++++++++- site-docs/cost.md | 31 +++++++++++++-- site-docs/limitations.md | 21 ++++++++++ site-docs/llm-tools.md | 47 +++++++++++++++++++++++ 8 files changed, 284 insertions(+), 7 deletions(-) create mode 100644 site-docs/bayesian.md create mode 100644 site-docs/calibrate.md create mode 100644 site-docs/llm-tools.md diff --git a/mkdocs.yml b/mkdocs.yml index 80c1b7b..b40339a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -62,7 +62,7 @@ plugins: A Home Assistant custom integration, installed through HACS. Questions become sensors: a probability, one of a set of options, or a number on a scale. Four actions (jev.noul, jev.choice, jev.score, jev.ask) answer inside an - automation, an AI Task entity answers ai_task.generate_data, and a + automation, jev.calibrate fits a threshold from history, an AI Task entity answers ai_task.generate_data, and a conversation agent handles Assist. Every call is billed in input tokens against an optional daily budget. sections: @@ -79,10 +79,13 @@ plugins: - actions.md: The four actions, their fields and their response variables - ai-task.md: The ai_task.generate_data entity and the structures it accepts - examples.md: Worked automations + - calibrate.md: jev.calibrate, a threshold measured from recorder history Voice: - conversation.md: The Assist conversation agent and what it will not control + - llm-tools.md: The jev__noul and jev__choice tools for other LLM agents Reference: - - cost.md: Token billing, the usage sensors and the daily budget + - bayesian.md: When to use Jev and when the Bayesian binary sensor + - cost.md: Token billing, the usage sensors, a month worked out and the daily budget - measurements.md: Latency and answer quality measured against the live API - troubleshooting.md: Debug logging and the common failures - limitations.md: What Jev does not do @@ -119,9 +122,12 @@ nav: - Actions: actions.md - AI Task: ai-task.md - Examples: examples.md + - Tuning a threshold: calibrate.md - Voice: - Conversation agent: conversation.md + - Tools for other LLM agents: llm-tools.md - Reference: + - Jev and the Bayesian sensor: bayesian.md - What it costs: cost.md - Measurements: measurements.md - Troubleshooting: troubleshooting.md diff --git a/site-docs/actions.md b/site-docs/actions.md index 5e45f0d..b22e7e1 100644 --- a/site-docs/actions.md +++ b/site-docs/actions.md @@ -109,6 +109,12 @@ the five you might need and discarding four is cheaper than two round trips. An object arrives as an object, not as a stringified dict. The model reads the field names as labels, so name them for what they hold. +## Tune a threshold + +`jev.calibrate` is a fifth action that asks nothing. It compares the recorded history +of a noul sensor with an entity that shows what was really true, and returns the +threshold that fits best. It costs no tokens. See [tuning a threshold](calibrate.md). + ## Errors name the limit Every budget failure names the budget, the limit and your ask, because an agent diff --git a/site-docs/bayesian.md b/site-docs/bayesian.md new file mode 100644 index 0000000..22c6430 --- /dev/null +++ b/site-docs/bayesian.md @@ -0,0 +1,73 @@ +# Jev and the Bayesian sensor + +Home Assistant already has a sensor that turns several readings into one probability, +the [Bayesian binary sensor](https://www.home-assistant.io/integrations/bayesian/). +Both give you a number from 0 to 1 and a threshold that turns it into on or off. They +get the number in different ways, and that decides which one fits. + +## How each gets its number + +The Bayesian sensor multiplies probabilities you give it. You set a prior, and for +each observation you say how often it is seen when the answer is yes +(`prob_given_true`) and when it is no (`prob_given_false`). An observation is a state, +a numeric range or a template. The sensor combines them with Bayes' rule and treats +them as independent of each other. + +Jev reads the states and your question in words, and the model gives the probability. +You write no probabilities. You write what the question means, and optionally what +counts as yes and as no. + +## Side by side + +| | Bayesian sensor | Jev noul | +|---|---|---| +| You supply | a prior and two probabilities per observation | a question in words | +| Where it runs | in Home Assistant | a call to the TypeSafe API | +| Cost | nothing | input tokens, see [what it costs](cost.md) | +| Time to update | when a state changes | 250 to 580 ms warm, measured from the Netherlands | +| Same input, same output | yes | no. One sentence gave 0.25, 0.28 and 0.31 in three runs | +| Reads free text | only through a template you write | yes, a note or a transcript is ordinary input | +| Observations that depend on each other | counted as independent, so they count twice | read together | +| Why it said what it said | each observation's share is visible | a number with no reasoning | + +## Which to use + +Use the Bayesian sensor when you know, or can measure, how often each reading goes with +the answer, and the readings are few and mostly independent. "Somebody is home" from a +phone, a door and motion in the hall is the textbook case. It costs nothing, it works +without the internet, and you can see why it changed. + +Use Jev when the probabilities are the part you cannot write down. A washing machine +whose power draw rises and falls through a programme, a doorbell transcript, or three +readings that only mean something together are hard to express as +`prob_given_true`. Jev takes them as they are. + +If you already have a working Bayesian sensor, keep it. Jev does not make one wrong. + +## Using both + +A Jev noul sensor is a number, so a Bayesian sensor can use it as a `numeric_state` +observation, next to readings that it handles well on its own: + +```yaml +binary_sensor: + - platform: bayesian + name: Laundry waiting + prior: 0.2 + probability_threshold: 0.8 + observations: + - platform: numeric_state + entity_id: sensor.jev_laundry_done + above: 0.6 + prob_given_true: 0.9 + prob_given_false: 0.1 + - platform: state + entity_id: binary_sensor.laundry_door + to_state: "off" + prob_given_true: 0.95 + prob_given_false: 0.5 +``` + +The probabilities in that example are placeholders. Measure your own, for example with +[jev.calibrate](calibrate.md), which reports precision and recall for a Jev sensor +against an entity that shows what was really true. diff --git a/site-docs/calibrate.md b/site-docs/calibrate.md new file mode 100644 index 0000000..dbabd07 --- /dev/null +++ b/site-docs/calibrate.md @@ -0,0 +1,80 @@ +# Tuning a threshold + +A noul is a probability, and the threshold that turns it into yes or no is your +choice. 0.5 is where the model says it cannot tell. It is not where your washing +machine is done. `jev.calibrate` finds a threshold from what really happened in your +house. + +## What it needs + +- a probability sensor, such as a Jev noul sensor +- an entity that shows what was really true, such as a door contact, or a smart plug + that reports when a machine runs +- the recorder, which keeps the history of both + +It reads the recorder and nothing else. It does not call TypeSafe, so it costs no +tokens. + +```yaml +- action: jev.calibrate + response_variable: fit + data: + entity_id: sensor.jev_laundry_done + truth_entity_id: binary_sensor.laundry_door + truth_state: "off" + days: 7 +``` + +| Field | Default | Meaning | +|---|---|---| +| `entity_id` | required | The probability sensor | +| `truth_entity_id` | required | The entity that shows what was true | +| `truth_state` | `on` | The state of that entity that means yes | +| `days` | 7 | How far back to read. The recorder keeps 10 days unless you changed `purge_keep_days` | + +## What it returns + +```yaml +threshold: 0.31 +precision: 1.0 +recall: 1.0 +f1: 1.0 +hours: 4.0 +hours_true: 2.0 +times_true: 1 +probability_changes: 4 +table: + - {threshold: 0.1, precision: 0.5, recall: 1.0, f1: 0.667} + # ... one row for each tenth up to 0.9 +``` + +The action cuts the window at every change of either entity, and counts time, not +state changes: + +- **precision** is the part of the time the threshold said yes that was really true. + A low precision means false alarms. +- **recall** is the part of the true time where the threshold said yes. A low recall + means missed cases. +- **f1** balances the two. The action tries every hundredth from 0.01 to 0.99 and + returns the one with the highest f1. When several tie, it returns the middle one, so + a probability a little off its usual values still lands on the same side. + +Use `table` when you care more about one side. For a notification you would rather +miss than repeat, pick a row with higher precision. + +## How much to trust it + +Read `times_true` before you read `f1`. One wash is one occasion, and an f1 of 1.0 over +one occasion tells you almost nothing. A week with a machine that runs every other day +gives three or four. Collect more occasions before you move a threshold far. + +Time when the probability sensor has no number, for example while the API was down, +is left out. + +## When it refuses + +| Error | Why | +|---|---| +| The recorder is not running | The action has nothing to read | +| No history | No time in the window has both a number and a truth state | +| Never true, or always true | With only one side, every threshold gets the same score | diff --git a/site-docs/conversation.md b/site-docs/conversation.md index 0279310..1ca6361 100644 --- a/site-docs/conversation.md +++ b/site-docs/conversation.md @@ -52,6 +52,7 @@ hallway" would reach a lock exposed there and unlock it. | An entity you did not expose to Assist | Never described to the model at all | | No room and no device named | Refused, unless you allow it. `turn_off` is exempt. Below the confidence floor, fallback | | Two kinds of device, no kind named, whole house | Asks which kind. With one kind exposed, it acts on that kind | +| Two devices fit the name about equally | Asks which one, see below | | A spent budget, a rejected key or no answer | Fallback. With no fallback agent it says which of the three it was | !!! info "It only sees what Assist sees" @@ -60,7 +61,23 @@ hallway" would reach a lock exposed there and unlock it. widen that. This is the voice path only. The [four actions](actions.md) send whatever an automation targets, exposed or not. -## What it costs +## When two devices fit the name + +"Turn on the lamp" with a desk lamp and a floor lamp exposed can split the model's +probability between the two, with neither above the confidence floor. When exactly two +exposed devices each have at least 0.2, and the two together reach the floor, the agent +asks **"Do you mean Desk lamp or Floor lamp?"** and keeps the conversation open. When +the two have the same name, it adds the room: "Lamp (Office) or Lamp (Bedroom)". + +Your reply, such as "the desk one", is one more request, and it counts against the +budget. It asks which of the two the reply picks. A confident pick carries out the +first command on that device. A reply that picks neither is handled as a new command. +The question expires after five minutes, the same time Home Assistant keeps a +conversation open. + +The 0.2 is not measured. It keeps a third device with a small share from turning the +question into a guess between two. + Every command counts against the same daily token budget as your sensors. A satellite that mishears a wake word all night trips that tripwire instead of running @@ -118,6 +135,10 @@ only sees what Jev could not route, which is the cheap arrangement. ## Diagnostics +The Assist debug view (**Settings**, **Voice assistants**, the pipeline's menu, +**Debug**) shows what Jev answered for each command: the model, every answer with its +probabilities, and the reason for the decision. + The last 20 decisions the agent made are in the integration's diagnostics, with the reason for every decision and the action distribution behind it. The sentence itself is redacted, because the file is meant to be pasted into an issue. diff --git a/site-docs/cost.md b/site-docs/cost.md index a8c7d0c..d192230 100644 --- a/site-docs/cost.md +++ b/site-docs/cost.md @@ -43,6 +43,29 @@ roughly 110 tokens per entity per call. thousands of tokens on every evaluation, which is why it is off by default. Access tokens, entity pictures and coordinates are never sent, even with it on. +## A month, worked out + +The formula is calls per month, times input tokens per call, times the price. The +examples use the published $0.042 per million input tokens and a 30 day month. + +| Setup | Calls a month | Tokens a call | A month | +|---|---|---|---| +| One context on 2 entities, every 5 minutes | 8,640 | about 405 | $0.15 | +| 20 spoken commands a day, 5 entities exposed | 600 | 1,371 at most | $0.035 | +| One context on 250 entities, every 5 minutes | 8,640 | about 16,500 | $5.99 | + +Where the token counts come from: + +- A request with one entity measured 339 input tokens, and each entity record adds + 65.8, so two entities are about 405. See [what an entity costs](measurements.md#what-an-entity-costs). +- A spoken command with five entities exposed measured 1,329 to 1,371 input tokens. +- 250 entities is the cap on a target, at 65.8 tokens each. + +Every 5 minutes is the default scan interval, and it is the most a context on a +schedule asks. A context that wakes on entity changes asks at most once every 30 +seconds, so a busy one can ask up to 10 times as often. Your own numbers are in +`sensor.jev_input_tokens_today` and `sensor.jev_estimated_cost_today`. + ## The budget is a tripwire Set **Daily input token budget** in the integration options. The check runs before @@ -89,10 +112,10 @@ that either of those cleared would not be a daily budget. - YAML contexts name no entry, so they belong to the first enabled Jev entry. A second entry does not ask them again and bill them twice. -The two one-off paths, a conversation command and the preview in the question editor, -still check what has already been spent rather than estimating the call ahead. Both -are started by a person and both have somewhere to fall back to, so the worst case is -one request over the line rather than a runaway. +A conversation command, an action and an AI Task estimate their call ahead, like a +context does. The preview in the question editor checks only what has already been +spent. A person starts it and it has nothing to fall back to, so the worst case is one +request over the line rather than a runaway. ### How the estimate is worked out diff --git a/site-docs/limitations.md b/site-docs/limitations.md index 2c1e0d4..d8e29f3 100644 --- a/site-docs/limitations.md +++ b/site-docs/limitations.md @@ -61,6 +61,27 @@ Locks are refused on purpose: Home Assistant reads turn_on on a lock as `lock.lo which is the opposite way round from the spoken command. Garage, gate and door covers are refused for the same reason. +## No local model + +Jev needs the TypeSafe API. There is no local mode. + +A llama.cpp fork adds a `POST /v1/decision` endpoint that answers a schema of enum, +boolean and number fields in one batched pass, with a probability for each answer. I +read its interface to see whether Jev could use it as a local backend. It cannot, as +the fork is now: + +- It returns only the probability of the value it chose, not the distribution over + all values. +- A noul maps exactly, because the probability of yes follows from the probability + of the chosen value. +- A choice needs the probability of every option, and a score's level is a weighted + average over all levels, so neither can be built from one probability. +- The request shape is different, so Jev would need a second client. + +If that endpoint returns the full distribution for each field, a local backend is +worth doing. Until then, only the yes/no questions could run locally, with the other +two types still going to TypeSafe. + ## It is not a core integration `quality_scale.yaml` tracks this against Home Assistant's quality scale at 47 done diff --git a/site-docs/llm-tools.md b/site-docs/llm-tools.md new file mode 100644 index 0000000..6abb88b --- /dev/null +++ b/site-docs/llm-tools.md @@ -0,0 +1,47 @@ +# Tools for other LLM agents + +An LLM conversation agent can read your house through the Assist API. What it cannot +give is a probability. Ask it whether the washing machine is done, and it says yes or +no. With this option on, the agent can hand that one question to Jev and get the +number back. + +## Turn it on + +**Settings**, **Devices & services**, **Jev**, **Configure**, then **Offer Jev as a +tool to other LLM agents**. It is off by default. + +It is off because every LLM agent that uses Assist gets the tool descriptions in every +prompt. That costs tokens at that agent's provider on every turn, whether the agent +calls a tool or not. + +## The two tools + +| Tool | The agent gives | The agent gets | +|---|---|---| +| `jev__noul` | `question`, optional `facts` | `probability_yes`, 0 to 1 | +| `jev__choice` | `question`, `options` (2 or more), optional `facts` | `choice`, `probabilities`, `confidence` | + +Each tool call is a call to [jev.noul or jev.choice](actions.md). It counts against +the same daily budget, and it shows in the same usage sensors. + +## What Jev sees + +Jev judges the entities exposed to that assistant, the same list the agent itself +reads, up to 150. `facts` carries anything else the question depends on, such as what +the user said. + +Jev sends `facts` as it is. The actions render a string `state` as a template, and the +tools do not, because a template could read an entity that you did not expose to +Assist. + +## More than one entry + +When two or more Jev entries have the option on, each tool has an `account` +parameter, and the agent must name the entry that pays. Each entry has its own key and +its own budget, so Jev does not choose one for the agent. + +## The Jev conversation agent + +The Jev conversation agent does not use these tools. It asks Jev directly, and it +falls back to another agent for what it cannot route. See +[conversation agent](conversation.md). From ffa47bdcadd5e66a8306b4042c49eea65782deea Mon Sep 17 00:00:00 2001 From: AboveColin Date: Thu, 24 Sep 2026 08:33:17 +0200 Subject: [PATCH 2/5] docs: ask-back from names, the Assist dialog note, the budget refusal The ask-back section described the probability split. It now describes the name comparison. It also restores the "What it costs" heading that the section had replaced. The diagnostics section named the debug view as the place for the agent detail. The note now shows in the Assist dialog and as an intent-progress event in the pipeline debug view. The refusal row and the budget sensor now say what the budget actually refuses: a command whose estimate does not fit, with tokens left. --- site-docs/conversation.md | 34 +++++++++++++++++++++------------- site-docs/cost.md | 4 +++- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/site-docs/conversation.md b/site-docs/conversation.md index 1ca6361..3d322a2 100644 --- a/site-docs/conversation.md +++ b/site-docs/conversation.md @@ -52,8 +52,8 @@ hallway" would reach a lock exposed there and unlock it. | An entity you did not expose to Assist | Never described to the model at all | | No room and no device named | Refused, unless you allow it. `turn_off` is exempt. Below the confidence floor, fallback | | Two kinds of device, no kind named, whole house | Asks which kind. With one kind exposed, it acts on that kind | -| Two devices fit the name about equally | Asks which one, see below | -| A spent budget, a rejected key or no answer | Fallback. With no fallback agent it says which of the three it was | +| Two devices whose names fit the command equally well | Asks which one, see below | +| Too little budget left for the command, a rejected key or no answer | Fallback. With no fallback agent it says which of the three it was, as an error reply | !!! info "It only sees what Assist sees" The agent describes only entities you exposed to Assist. You already decided @@ -63,11 +63,18 @@ hallway" would reach a lock exposed there and unlock it. ## When two devices fit the name -"Turn on the lamp" with a desk lamp and a floor lamp exposed can split the model's -probability between the two, with neither above the confidence floor. When exactly two -exposed devices each have at least 0.2, and the two together reach the floor, the agent -asks **"Do you mean Desk lamp or Floor lamp?"** and keeps the conversation open. When -the two have the same name, it adds the room: "Lamp (Office) or Lamp (Bedroom)". +The agent compares what you said with the names of the exposed devices of the kind +the model picked. The whole name said wins: with a "Lamp" and a "Desk lamp", "turn on +the lamp" is the Lamp and "turn on the desk lamp" is the Desk lamp. When two names fit +equally well, as "the lamp" does for a Desk lamp and a Floor lamp, the agent asks +**"Do you mean Desk lamp or Floor lamp?"** and keeps the conversation open. When the +two have the same name, it adds the room: "Lamp (Office) or Lamp (Bedroom)". A room +you name settles it first, so "the lamp in the office" acts. + +The names decide this, not the model's confidence. On a test instance with two lights +both called "Lamp", the model put 1.00 on one of them, every time, so a confidence +split never showed that there were two. With three or more that fit equally well, the +command goes to the fallback agent. Your reply, such as "the desk one", is one more request, and it counts against the budget. It asks which of the two the reply picks. A confident pick carries out the @@ -75,9 +82,7 @@ first command on that device. A reply that picks neither is handled as a new com The question expires after five minutes, the same time Home Assistant keeps a conversation open. -The 0.2 is not measured. It keeps a third device with a small share from turning the -question into a guess between two. - +## What it costs Every command counts against the same daily token budget as your sensors. A satellite that mishears a wake word all night trips that tripwire instead of running @@ -135,9 +140,12 @@ only sees what Jev could not route, which is the cheap arrangement. ## Diagnostics -The Assist debug view (**Settings**, **Voice assistants**, the pipeline's menu, -**Debug**) shows what Jev answered for each command: the model, every answer with its -probabilities, and the reason for the decision. +In the Assist dialog, each reply from Jev has a note under it with what Jev answered: +the decision and its reason, the slots, each answer with its top three options, the +model, the input tokens and the time the call took. The pipeline's debug view +(**Settings**, **Voice assistants**, the pipeline's menu, **Debug**) keeps the same +note as an `intent-progress` event of the run. The note goes to the pipeline only. It +is not added to the conversation, so a fallback agent does not read it. The last 20 decisions the agent made are in the integration's diagnostics, with the reason for every decision and the action distribution behind it. The sentence itself diff --git a/site-docs/cost.md b/site-docs/cost.md index d192230..1ea4df1 100644 --- a/site-docs/cost.md +++ b/site-docs/cost.md @@ -83,7 +83,9 @@ When it trips: - the answer sensors of that context go unavailable, because Jev was not asked and there is no answer for right now. The last answers are not thrown away, and the next call that fits replaces them -- `binary_sensor.jev_daily_budget_exceeded` turns on +- `binary_sensor.jev_daily_budget_exceeded` turns on. It shows that contexts have + stopped, so a spoken command, an action or an AI Task that the budget refuses + does not turn it on. Each of those says so to whoever started it - a repair issue explains it, naming the budget, what has been used, and the context that was refused with the tokens it needed From 6efa72a4caf2f57c33444da829e9d7ffc27c2e4e Mon Sep 17 00:00:00 2001 From: AboveColin Date: Thu, 24 Sep 2026 09:03:12 +0200 Subject: [PATCH 3/5] Say what a reply that asks for something else does --- site-docs/conversation.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/site-docs/conversation.md b/site-docs/conversation.md index 3d322a2..68c6827 100644 --- a/site-docs/conversation.md +++ b/site-docs/conversation.md @@ -72,13 +72,17 @@ two have the same name, it adds the room: "Lamp (Office) or Lamp (Bedroom)". A r you name settles it first, so "the lamp in the office" acts. The names decide this, not the model's confidence. On a test instance with two lights -both called "Lamp", the model put 1.00 on one of them, every time, so a confidence -split never showed that there were two. With three or more that fit equally well, the -command goes to the fallback agent. +both called "Lamp", the model put 1.00 on one of them in one session. In another it put +0.55 on "none of these", 0.44 on one Lamp and 0.01 on the other. Neither split showed +that there were two. With three or more that fit equally well, the command goes to the +fallback agent. Your reply, such as "the desk one", is one more request, and it counts against the -budget. It asks which of the two the reply picks. A confident pick carries out the -first command on that device. A reply that picks neither is handled as a new command. +budget. It asks which of the two the reply picks, and if the reply asks for something +of its own. A confident pick carries out the first command on that device. A reply +that picks neither, or that is a new instruction, is handled as a new command. So +"never mind, turn off the lamp in the bedroom" turns that lamp off, and does not run +the first command on it. The question expires after five minutes, the same time Home Assistant keeps a conversation open. From 6b7168fb294735e92c598de23d51ef9c3d55688b Mon Sep 17 00:00:00 2001 From: AboveColin Date: Thu, 24 Sep 2026 11:38:48 +0200 Subject: [PATCH 4/5] Say that the satellite's room settles a shared name --- site-docs/conversation.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site-docs/conversation.md b/site-docs/conversation.md index 68c6827..a5ff0b8 100644 --- a/site-docs/conversation.md +++ b/site-docs/conversation.md @@ -69,7 +69,9 @@ the lamp" is the Lamp and "turn on the desk lamp" is the Desk lamp. When two nam equally well, as "the lamp" does for a Desk lamp and a Floor lamp, the agent asks **"Do you mean Desk lamp or Floor lamp?"** and keeps the conversation open. When the two have the same name, it adds the room: "Lamp (Office) or Lamp (Bedroom)". A room -you name settles it first, so "the lamp in the office" acts. +you name settles it first, so "the lamp in the office" acts. Next comes the room of +the satellite that heard you, as for Home Assistant's own agent: "turn on the lamp" +said to the office satellite turns on the office Lamp. The names decide this, not the model's confidence. On a test instance with two lights both called "Lamp", the model put 1.00 on one of them in one session. In another it put From e47c2155321e6bdbe1afae115a4a9ce445fe31fc Mon Sep 17 00:00:00 2001 From: AboveColin Date: Thu, 24 Sep 2026 11:45:05 +0200 Subject: [PATCH 5/5] Count the 1.16 tree in the quality scale, and 1.16.0 --- custom_components/jev/manifest.json | 2 +- quality_scale.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/custom_components/jev/manifest.json b/custom_components/jev/manifest.json index 439ca71..1a3e2ea 100644 --- a/custom_components/jev/manifest.json +++ b/custom_components/jev/manifest.json @@ -10,5 +10,5 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/AboveColin/HA-Jev/issues", "requirements": ["jevclient==1.2.0"], - "version": "1.15.1" + "version": "1.16.0" } diff --git a/quality_scale.yaml b/quality_scale.yaml index fc7bd97..448028b 100644 --- a/quality_scale.yaml +++ b/quality_scale.yaml @@ -87,7 +87,7 @@ rules: agent all start it when TypeSafe rejects the key. test-coverage: status: done - comment: 97% measured across 407 tests, enforced in CI. + comment: 97% measured across 474 tests, enforced in CI. # Gold devices: done @@ -133,12 +133,12 @@ rules: placeholder. A question sensor is named by the user's own question text, which is theirs to write and cannot be translated by anyone else. The conversation agent and the AI Task entity take the device name, which is what every core - provider of either does, and the ten sentences the agent speaks itself go + provider of either does, and the twelve sentences the agent speaks itself go through the common translation category, so a Dutch pipeline answers in Dutch. exception-translations: status: done comment: >- - Twenty-eight keys, including the library's own limit errors, which are + Thirty-four keys, including the library's own limit errors, which are re-raised with the real numbers as placeholders rather than passed through in English. The coordinator's UpdateFailed and setup's ConfigEntryNotReady and ConfigEntryAuthFailed are translated too, since both reach the integrations @@ -148,7 +148,7 @@ rules: icon-translations: status: done comment: >- - icons.json covers every translated entity and all four actions, and a test + icons.json covers every translated entity and all five actions, and a test asserts that. The conversation entity takes the conversation domain's own icon, as core's agents do. reconfiguration-flow: @@ -176,4 +176,4 @@ rules: Assistant ships aiohttp and httpx, not httpx2. strict-typing: status: done - comment: mypy --strict passes on all 20 modules, enforced in CI. + comment: mypy --strict passes on all 22 modules, enforced in CI.