Versatile Thermostat + Home Assistant MCP

This documentation describes how to add LLM/MCP tools to Versatile Thermostat (VTherm) through Home Assistant's official MCP server.

The provided llm.py file exposes:

  • 6 read/diagnostic, log and history tools;
  • 3 simple action tools: temperature, preset and HVAC mode;
  • 2 read-only TPI tools and 1 explicit TPI write tool;
  • descriptions and instructions usable in French and English;
  • links to the VTherm documentation, log collector and log analyzer for troubleshooting.

The file does not create its own MCP server. It uses Home Assistant's native LLM platform, and the official Model Context Protocol Server then publishes these tools to MCP clients.


1. File to install

The following modules must be present together in the integration directory:

/config/custom_components/versatile_thermostat/llm.py
/config/custom_components/versatile_thermostat/llm_diagnostics.py
/config/custom_components/versatile_thermostat/llm_diagnostic_time.py
/config/custom_components/versatile_thermostat/recorder_export.py
/config/custom_components/versatile_thermostat/tpi_history.py
/config/custom_components/versatile_thermostat/tpi_identification.py

Install these modules from the same revision. TPIHistory owns one thermostat's Home Assistant context and sources, while TPIIdentification owns its grid and analysis parameters; the two llm_diagnostic* modules provide log and plugin reads.

Directory structure:

/config/custom_components/versatile_thermostat/
├── __init__.py
├── climate.py
├── const.py
├── manifest.json
├── ...
├── llm.py
├── llm_diagnostics.py
├── llm_diagnostic_time.py
├── recorder_export.py
├── tpi_history.py
└── tpi_identification.py

There is no need to modify manifest.json to declare the llm platform.

Home Assistant discovers integration platforms dynamically and loads llm.py on demand when an LLM API needs the integration's tools.

After copying the file, perform a full restart of Home Assistant.

Warning: if llm.py was added manually to a VTherm installation managed by HACS, a future HACS update may replace the integration directory. Keep a copy of the file until it is integrated into the VTherm project itself.


2. Exposed tools

With the assist LLM API, the Home Assistant MCP server adds the assist__ prefix to tools.

Read-only — level 1

assist__versatile_thermostat__list_vtherms
assist__versatile_thermostat__get_status
assist__versatile_thermostat__diagnose
assist__versatile_thermostat__get_history
assist__versatile_thermostat__get_logs
assist__versatile_thermostat__get_plugins

Actions — level 2A

assist__versatile_thermostat__set_temperature
assist__versatile_thermostat__set_preset
assist__versatile_thermostat__set_hvac_mode

The read-only tools are marked:

read_only=True
destructive=False
idempotent=True
open_world=False

The three action tools are marked:

read_only=False
destructive=False
idempotent=True
open_world=False

They actually modify the thermostat state, but only through the standard Home Assistant climate.* services.


Logs and plugins

get_logs reads the existing in-memory collector, not Recorder history. It takes entity_id, optional start_time and end_time in ISO format with an explicit offset (2026-11-08T02:00:00+02:00 or 2026-11-08T00:00:00Z), and min_level of DEBUG, INFO, WARNING, or ERROR (default DEBUG). The end defaults to the query time; the start defaults to one hour before the effective end. Bounds are inclusive, with no maximum period or pagination.

Selection includes messages without a thermostat hint and follows the collector's existing name matching. Above 5,000 selected entries, the last 5,000 are returned in collector order with selected, returned and omitted counts and an explicit truncation flag. An empty or untruncated result does not guarantee historical completeness: eviction and restart can remove entries. The cap guarantees neither a full hour nor a maximum response size.

get_plugins takes entity_id and checks configured links for SmartPI, Hysteresis, Pellet Regulation, Auto-fan, Heating Failure Detection, Climate Replication and Inhibition. It distinguishes configuration, loading, availability and published diagnostics where known. Unavailable sources, unresolved identities or producers, and unknown fields remain explicit; partial state does not establish absence or healthy operation. Future plugins may not be detectable and the list is not an exhaustive third-party inventory. The read triggers no calculation, refresh or command.

Source publication times are UTC ISO strings with Z; separate raw values and knowledge fields preserve the original and any timezone uncertainty. Hysteresis activity, when known, is the published logical controller state, not proof of physical heating. A sensor belonging to a different configuration entry is not attributed to the identified plugin.

Both tools target a loaded VTherm exposed to Assist. Actual client availability depends on Home Assistant/MCP compatibility and channel behavior; interpreting logs and diagnostics remains the agent's responsibility.


3. list_vtherms

This tool only lists VTherm entities that are:

  • loaded in Home Assistant;
  • in the climate domain;
  • associated with a Versatile Thermostat configuration entry;
  • exposed to Assist.

For each thermostat, it returns, among other things:

entity_id
name
hvac_mode
hvac_action
preset_mode
current_temperature
target_temperature
target_minus_current
min_temperature
max_temperature
target_temperature_step
hvac_modes
preset_modes
thermostat_type
messages

Simplified example:

{
  "entity_id": "climate.thermostat_salon",
  "hvac_mode": "heat",
  "hvac_action": "heating",
  "preset_mode": "comfort",
  "current_temperature": 18.7,
  "target_temperature": 19.5,
  "target_minus_current": 0.8,
  "hvac_modes": ["heat", "off"],
  "preset_modes": ["none", "frost", "eco", "comfort", "boost"]
}

4. get_status

get_status returns the complete raw state of the VTherm entity:

entity_id
state
last_changed
last_updated
attributes

attributes contains the Home Assistant attributes and the VTherm-specific attributes available on the entity.

This tool is useful when the agent needs information that was not extracted into the structured diagnostic output.


5. diagnose

diagnose is the preferred tool for requests such as:

  • "Why is my thermostat not heating?"
  • "Why is it idle?"
  • "Is it being shed?"
  • "Is there a window-related issue?"
  • "Why did VTherm stop?"
  • "Debug the living-room thermostat."
  • "Diagnose why this VTherm is not heating."

It returns, among other things:

Current state

hvac_mode
hvac_action
preset_mode
current_temperature
target_temperature
target_minus_current
hvac_modes
preset_modes

VTherm reasons

The file prioritizes information calculated by VTherm itself:

specific_states.messages
hvac_off_reason
hvac_mode_reason
should_device_be_active
device_actives
unavailable_underlying_entities
temperature_slope
is_sleeping
is_recalculate_scheduled

The goal is to prevent the model from inventing a cause based only on the difference between the current temperature and the target temperature.

Diagnostic sections

When available, the following sections are returned in full:

configuration
current_state
requested_state
specific_states
preset_temperatures
presence_manager
motion_manager
power_manager
window_manager
safety_manager
auto_start_stop_manager
lock_manager
heating_failure_detection
vtherm_over_switch
vtherm_over_climate
vtherm_over_valve

A section that is absent from the entity is simply not returned.


6. VTherm documentation and troubleshooting

The diagnose result also contains support_resources with both French and English resources:

documentation_fr
documentation_en
reference_fr
reference_en
log_collector_fr
log_collector_en
log_analyzer
official_repository
log_download_action

Main links:

For complex debugging, the prompt supplied to the LLM asks it to:

  1. use the information returned by VTherm first;
  2. not invent a cause that is not present in the data;
  3. consult the French or English documentation matching the language of the request if the client has Web access;
  4. suggest the log collector and then the log analyzer if the instantaneous state is not enough to reach a conclusion.

Important

llm.py does not download Web pages itself.

It provides URLs and instructions to the LLM client. If the client being used (Codex, Gemini, Claude, etc.) has Web access, it can consult the documentation. Otherwise, it can at least provide the links to the user.

The versatile-thermostat.org website itself states that it is a community website and specifies that the official source of the project remains the VTherm GitHub repository.


7. Log collector

VTherm provides the action:

versatile_thermostat.download_logs

The llm.py file does not execute this action automatically; it reports it as a troubleshooting resource.

Home Assistant example:

action: versatile_thermostat.download_logs
data:
  entity_id: climate.thermostat_salon
  log_level: DEBUG

The resulting file can then be loaded into the analyzer:

https://www.versatile-thermostat.org/fr/debugger/

Level 2 — actions

8. set_temperature

Uses:

climate.set_temperature

MCP parameters:

{
  "entity_id": "climate.thermostat_salon",
  "temperature": 19.5
}

Before the call, the tool checks:

  • that the VTherm is exposed to Assist;
  • that an authenticated Home Assistant user is associated with the call;
  • that the user has permission to control this entity;
  • that the requested temperature remains within the thermostat's min/max values when they are available.

The result contains:

success
action
requested
state_after_call
note

state_after_call allows the model to verify the state immediately after the command.

VTherm may then change the effective state according to its own rules: central mode, window, power shedding, safety, preset, auto start/stop, regulation, etc.


9. set_preset

Uses:

climate.set_preset_mode

Parameters:

{
  "entity_id": "climate.thermostat_salon",
  "preset": "comfort"
}

Before the action, the requested preset must appear in:

preset_modes

for the targeted entity.

A model therefore cannot arbitrarily request a preset that is not available on that thermostat.


10. set_hvac_mode

Uses:

climate.set_hvac_mode

Parameters:

{
  "entity_id": "climate.thermostat_salon",
  "hvac_mode": "heat"
}

The mode must be present in:

hvac_modes

To turn off a thermostat when off is supported:

{
  "entity_id": "climate.thermostat_salon",
  "hvac_mode": "off"
}

The file deliberately exposes no toggle tool: the desired state must be explicit.


11. Action safety

The three climate actions above apply these safeguards (the TPI action is described separately):

  1. only VTherm entities exposed to Assist;
  2. a Home Assistant user context is mandatory for write operations;
  3. CONTROL permission is checked;
  4. presets and HVAC modes are validated against the real entity;
  5. the temperature range is validated;
  6. only standard climate.* services are used;
  7. no VTherm internal configuration is modified;
  8. no TPI, power shedding, window, safety or algorithm setting is changed.

Actions are marked read_only=False, which allows the Home Assistant MCP server to hide them when its Read only option is enabled.


Installing the official Home Assistant MCP server

12. Enable the MCP server

In Home Assistant:

Settings
→ System
→ AI
→ Model Context Protocol

Enable the MCP server if necessary.

It can also be added through:

Settings
→ Devices & services
→ Add integration
→ Model Context Protocol Server

Home Assistant then provides an MCP URL, typically on a local network:

http://homeassistant.local:8123/api/mcp

In the Model Context Protocol Server options:

Expose all LLM APIs      : OFF (recommended to limit the exposed surface)
Control Home Assistant   : Assist / Device control
Require administrator    : ON
Read only                : OFF to use level 2

If Read only is enabled

The action tools:

set_temperature
set_preset
set_hvac_mode
set_tpi_coefficients

are filtered out of the MCP catalog because they are declared read_only=False.

The diagnostic tools remain available.

This is useful when you temporarily want to return to level 1 operation without modifying llm.py.


14. Expose VTherm entities to Assist

The tools only access thermostats exposed to Assist.

In Home Assistant, expose the required entities, for example:

climate.thermostat_salon
climate.thermostat_cuisine
climate.thermostat_salle

There is no need to expose every device in the home.


Connecting to Codex

15. Add the MCP server

codex mcp add homeassistant \
  --url http://homeassistant.local:8123/api/mcp

Codex opens Home Assistant in the browser for OAuth authentication.

Verification:

codex mcp list

Example:

homeassistant  http://homeassistant.local:8123/api/mcp  enabled

For installation and configuration of the official Home Assistant MCP server, see the Home Assistant reference documentation.


16. Check VTherm tools

Prompt:

Use only the homeassistant MCP server.
List all tools available on this server whose name contains
versatile_thermostat. Do not perform any action.

With level 2A, the following should be available:

assist__versatile_thermostat__list_vtherms
assist__versatile_thermostat__get_status
assist__versatile_thermostat__diagnose
assist__versatile_thermostat__get_history
assist__versatile_thermostat__set_temperature
assist__versatile_thermostat__set_preset
assist__versatile_thermostat__set_hvac_mode
assist__versatile_thermostat__check_tpi_optimization
assist__versatile_thermostat__analyze_tpi_coefficients
assist__versatile_thermostat__set_tpi_coefficients

The complete catalog has six read tools and four write tools. With Read only, only the six read tools remain visible.


Example questions

17. French — read and diagnose

Liste mes Versatile Thermostat et donne-moi leur température actuelle,
leur consigne et leur état de chauffe.
Diagnostique climate.thermostat_salon et explique pourquoi il ne chauffe pas.
N'invente aucune cause non présente dans les données VTherm.
Pourquoi le thermostat du salon est-il idle alors que la température est
inférieure à la consigne ? Regarde aussi le délestage, la sécurité et les fenêtres.
Fais un diagnostic de climate.thermostat_salon. Si les données ne suffisent pas,
indique quelle documentation VTherm consulter et rappelle-moi comment utiliser
le collecteur et l'analyseur de logs.

18. French — actions

Mets la consigne du thermostat du salon à 19,5 °C.
Passe climate.thermostat_salon en preset comfort.
Passe climate.thermostat_salon en mode HVAC off.
Remets climate.thermostat_salon en mode heat puis affiche son état.

19. English — read and diagnose

List my Versatile Thermostats and show their current temperature,
target temperature and HVAC action.
Diagnose climate.thermostat_salon and explain why it is not heating.
Do not infer a cause that is not supported by the VTherm diagnostic data.
Debug climate.thermostat_salon. Check power shedding, safety, window detection,
auto start/stop and unavailable underlying entities.
If the current diagnostic data is not enough, tell me which VTherm documentation
page or log-analysis workflow I should use next.

20. English — actions

Set climate.thermostat_salon to 19.5 °C.
Set climate.thermostat_salon preset to comfort.
Set climate.thermostat_salon HVAC mode to off.

Troubleshooting

21. No VTherm tool appears

Check:

  1. that the file exists here:

    /config/custom_components/versatile_thermostat/llm.py
    

/config/custom_components/versatile_thermostat/recorder_export.py /config/custom_components/versatile_thermostat/tpi_history.py /config/custom_components/versatile_thermostat/tpi_identification.py


2. Home Assistant has been restarted;
3. VTherm is loaded normally;
4. at least one VTherm `climate` entity is exposed to Assist;
5. the Assist/Device control API is exposed by the MCP server;
6. the MCP client has reconnected after the restart.

The `llm.py` platform is loaded on demand: the absence of a startup message in Home Assistant does not necessarily mean that it was not detected.

---

## 22. Read tools appear but action tools do not

Check the option:

```text
Model Context Protocol Server → Read only

For level 2A:

Read only = OFF

23. An action is rejected

The tool result normally indicates the reason:

  • no authenticated user context;
  • insufficient permission;
  • temperature outside the allowed range;
  • preset does not exist;
  • HVAC mode not supported;
  • error returned by the Home Assistant service.

Use list_vtherms or get_status to retrieve the values actually supported by the entity.


TPI coefficient optimization from history

A simple request is enough, for example: “Are the parents' bedroom thermostat coefficients optimal?” or “Are my coefficients well tuned?”. Tool descriptions guide the assistant to the read-only TPI check and calculation, resolving the room name when needed. Users need not name tools or supply an entity_id, and dashboard access is not a prerequisite. The selected connector must expose check_tpi_optimization and analyze_tpi_coefficients: rephrasing cannot make missing tools available. Results remain exploratory estimates, not proof of an optimum.

Three tools complement the existing tools:

Tool (suffix versatile_thermostat__…)AccessStage
check_tpi_optimizationRead-only1/3 — check
analyze_tpi_coefficientsRead-only2/3 — analysis
set_tpi_coefficientsExplicit write3/3 — optional application

Read tools take only entity_id. The client announces each stage before calling; no background work is simulated. Every response contains stage, stage_number, stage_total and state_changed.

Analysis requires classic TPI (proportional_function=tpi), with no configured Auto-TPI, active learning or continuous external compensation. This version supports over_switch with one or more physical switches. Recorded ON durations are integrated and averaged using VTherm's equal power share convention device_power / N, including staggered phases and subminute transitions. Absolute heater power is unnecessary. This convention does not measure unequal individual heater powers.

For over_climate, over_valve and climate valve regulation, the response explicitly returns eligible: false, supported: false, reason: unsupported_thermostat_type: analysis is not yet available for this type. Ambiguous virtual commands are also rejected; there is no blind hvac_action fallback.

Recorder supplies at most the last 14 days. At least 7 days of actually usable minutes are required after exclusions. Inside/outside sensors come from effective VTherm configuration, including central configuration. Results detail sources, history_requested_days, history_available_days, history_usable_days, exclusions and current settings. No Recorder retention period is assumed.

The response includes applicability_note (FR/EN): coefficients are exploratory estimates derived from the analyzed period, with suitability under other conditions not established. Rerun the analysis on a representative period if heating conditions or household use change substantially, or regulation deteriorates, before considering an adjustment. Outdoor temperature changes alone do not require recalculation.

The Kalman comparison is disabled in the MCP response: simulations show that coefficients within bounds can still be inaccurate. experimental_estimator reports unavailable with reason experimental_model_not_validated_for_application, without running or returning Kalman coefficients. The estimator remains available for offline research, rejecting values outside [0, 10] (internal) and [0, 1] (external), insufficient excitation and unstable estimates. Clipping never turns a failed fit into a usable result. These screens do not prove reliability; OLS remains exploratory and application is never automatic. Bootstrap intervals remain uncalibrated and are not truncated to service bounds.

Temperatures use a one-minute grid and causal hold limited to 60 minutes. Unlike the reference script's outdoor interpolation, no future measurement is used. HA binary states persist until their next event; unknown/unavailable invalidate the interval. Physical and diagnostic safety, power-shedding and window sensors are read through Recorder because climate diagnostic sections are often unrecorded. A required missing exclusion source causes refusal. Non-heat HVAC periods, active protections, open windows and missing data are excluded. Each resolution reports excluded window cycles. This version requires VTherm configured in °C; source sensors in °F are converted to °C.

The model preserves ΔT = g × (T_ext - T_int) + B × u + q, coef_ext = g/B, A = 1-g and coef_int = (A-exp(-dt/tau_cl))/B. Preferred convergence remains 6 h, with the 3/4/5/6/8/10/12 h table returned. Real cycle_min is primary, alongside 10/20/30/60-minute fits, with an explicit fallback to the nearest valid resolution. Each fit requires 40 cycles, 80% valid minutes and the reference physical bounds. Missing bins never silently become longer cycles.

Chronological 70% training / 30% validation, OLS numpy.linalg.lstsq, MAE, RMSE, ΔT R² and conditioning are retained. Temporal bootstrap uses 1000 repetitions, 24-hour blocks and seed 20261030; gaps do not compress the blocks. Approximate 95% confidence intervals are returned. Fewer than 30 accepted physical repetitions causes bootstrap_failed. Nonphysical fits or condition numbers ≥1000 are rejected; 100–1000 requires caution. Low R² alone is not a rejection criterion. Relative coef_ext span above 25%, fewer than three resolutions or a wide CI trigger warnings. recommendation_reliable: false means the candidate must not be described as reliable. No CSV, plot, report or other file is required or generated; pandas/matplotlib dependencies are not added.

Analysis never applies coefficients. Present results, limitations and comparison, then await the user's decision. An explicit direct change needs no history or analysis:

{"entity_id": "climate.thermostat_salon", "coef_int": 0.35, "coef_ext": 0.025}

At least one coefficient is required; partial updates are supported. VTherm service ranges: 0 <= coef_int <= 10, 0 <= coef_ext <= 1, finite values only. The tool rechecks Assist exposure, classic TPI, authenticated user and CONTROL, then calls only versatile_thermostat.set_tpi_parameters with that context. Existing central-configuration and lock restrictions remain enforced by the service. The service may persist settings; the tool never writes a ConfigEntry directly.

large_change means an absolute delta of 0.15 for coef_int, 0.01 for coef_ext, or a relative delta of 50% (any change from zero is large). These are confirmation thresholds, not thermal safety guarantees. The tool returns large_change_confirmation_required, confirmation_required: true, comparison and a confirmation_token, without writing. After additional explicit user confirmation, repeat the same values with confirm_large_change: true and the token. It expires after 10 minutes and is bound to the user, thermostat and exact comparison; changing current settings invalidates it.

After calling, values are reread from the loaded VTherm object, including after reload: previous, requested, applied. Failures after a service call return state_changed: null and require verification; they do not claim nothing changed. Pre-call refusals state that no coefficient was changed. Never bypass a refusal with generic services.

MCP Read only retains both analysis tools and hides set_tpi_coefficients, like the other actions.


Technical references


Recorder history: CSV exports and charts

For “Make a professional annotated graph of the kitchen thermostat over 48 h”, the assistant is guided to get_history, then client-side chart creation after fetching every page. ApexCharts/Lovelace YAML is offered only when dashboard configuration is explicitly requested. SmartPI climate history can be plotted even though TPI coefficient analysis rejects SmartPI. Annotations must come from recorded events; the tool does not expose historical SmartPI power or physical relay histories. Current values cannot reconstruct these series. If get_history is absent from the connector catalog, the assistant must explain that limitation.

The read-only versatile_thermostat__get_history tool sends the MCP client recorded history of the VTherm climate entity: state, measured temperature, target, HVAC action and preset, when recorded. It supports all VTherm types independently of TPI analysis. It does not expose the entire Recorder or associated sensor and relay histories.

The client (ChatGPT, Claude, Gemini, Codex, etc.) retrieves the data and creates CSV files or charts according to its capabilities. Examples: “Export one week of living-room history as CSV” or “Plot office temperature and target over two days”.

Parameters: entity_id, start_time, end_time (ISO 8601 with timezone), offset (default 0) and limit (default 500, maximum 500). A period spans at most 14 days; split longer periods. Fetch every page using next_offset and identical bounds until null. Results include rows, total_rows, next_offset, pagination_complete, UTC timestamps and available recorded attributes. Pagination is not a frozen Recorder snapshot.

Only VTherms still exposed to Assist are accessible, with an authenticated user holding read permission. Points represent recorded changes, not regular samples; Recorder may carry a state forward to the period start. Missing fields remain null, and unknown/unavailable stay explicit. Retention, exclusions and outages may limit data; completed pagination does not guarantee complete history. The client must disclose these limits and never invent missing values. No configuration is changed.