From 66b5d4a64a7193fcf38caa04aecc2257c7242cd5 Mon Sep 17 00:00:00 2001 From: claude Date: Wed, 22 Jul 2026 19:13:08 +0300 Subject: [PATCH] Fix thread-unsafe state writes; use conversation-agent dropdown - entity.py: decorate the dispatcher target _handle_update with @callback so it runs on the event loop. Without it, async_dispatcher_send offloaded the plain sync callback to an executor thread, and async_write_ha_state raised "calls async_write_ha_state from a thread other than the event loop". - config_flow.py: replace the free-text Assist agent field with ConversationAgentSelector (a proper agent dropdown), and use LanguageSelector for the language field. - README: clarify that the built-in Assist agent only handles device commands; an LLM conversation agent is required for free-form chat. Co-Authored-By: Claude Opus 4.8 --- README.md | 8 ++++++-- custom_components/reticulum/config_flow.py | 14 ++++++++------ custom_components/reticulum/entity.py | 5 +++++ custom_components/reticulum/manifest.json | 2 +- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 806b822..c03e5a1 100644 --- a/README.md +++ b/README.md @@ -95,8 +95,12 @@ will answer. ### Options (⚙ → Configure) - **Route incoming messages to Assist** – turn the chat bridge on/off. -- **Assist agent / language** – pick a specific conversation agent, or leave - blank for the default pipeline. +- **Assist agent / language** – pick the conversation agent from the dropdown. + > **Want free-form chat?** The default *Home Assistant* agent only recognises + > device-control commands and will answer anything else with "Sorry, I + > couldn't understand that". To actually chat, install an LLM conversation + > integration (OpenAI, Google Generative AI, Anthropic, Ollama, …), then + > select that agent here. - **Reply to any sender** / **Allowed sender addresses** – restrict who Assist will answer. - **Default recipient** – address used by the `notify.reticulum` entity. diff --git a/custom_components/reticulum/config_flow.py b/custom_components/reticulum/config_flow.py index b4e58eb..275a036 100644 --- a/custom_components/reticulum/config_flow.py +++ b/custom_components/reticulum/config_flow.py @@ -17,6 +17,10 @@ from homeassistant.core import callback from homeassistant.helpers import config_validation as cv from homeassistant.helpers.selector import ( BooleanSelector, + ConversationAgentSelector, + ConversationAgentSelectorConfig, + LanguageSelector, + LanguageSelectorConfig, NumberSelector, NumberSelectorConfig, NumberSelectorMode, @@ -170,16 +174,14 @@ class ReticulumOptionsFlow(OptionsFlow): ): BooleanSelector(), vol.Optional( CONF_ASSIST_AGENT, - description={ - "suggested_value": opts.get(CONF_ASSIST_AGENT, "") - }, - ): TextSelector(), + description={"suggested_value": opts.get(CONF_ASSIST_AGENT)}, + ): ConversationAgentSelector(ConversationAgentSelectorConfig()), vol.Optional( CONF_ASSIST_LANGUAGE, description={ - "suggested_value": opts.get(CONF_ASSIST_LANGUAGE, "") + "suggested_value": opts.get(CONF_ASSIST_LANGUAGE) }, - ): TextSelector(), + ): LanguageSelector(LanguageSelectorConfig()), vol.Required( CONF_ALLOW_ALL, default=opts.get(CONF_ALLOW_ALL, True) ): BooleanSelector(), diff --git a/custom_components/reticulum/entity.py b/custom_components/reticulum/entity.py index fd06f1a..8f21104 100644 --- a/custom_components/reticulum/entity.py +++ b/custom_components/reticulum/entity.py @@ -2,6 +2,7 @@ from __future__ import annotations +from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import DeviceInfo, Entity @@ -35,5 +36,9 @@ class ReticulumEntity(Entity): ) ) + @callback def _handle_update(self) -> None: + # Decorated with @callback so the dispatcher runs it on the event loop + # (a plain sync callback would be offloaded to an executor thread, and + # async_write_ha_state is not thread-safe). self.async_write_ha_state() diff --git a/custom_components/reticulum/manifest.json b/custom_components/reticulum/manifest.json index f8d37dc..cf2b6c7 100644 --- a/custom_components/reticulum/manifest.json +++ b/custom_components/reticulum/manifest.json @@ -12,5 +12,5 @@ "loggers": ["RNS", "LXMF"], "requirements": ["rns>=0.9.0", "lxmf>=0.6.0"], "single_config_entry": true, - "version": "1.0.3" + "version": "1.0.4" }