homeassistant 2025.7.0b7__py3-none-any.whl → 2025.7.0b9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- homeassistant/components/ai_task/translations/nl.json +3 -0
- homeassistant/components/alexa_devices/config_flow.py +3 -1
- homeassistant/components/alexa_devices/manifest.json +1 -1
- homeassistant/components/alexa_devices/strings.json +1 -0
- homeassistant/components/alexa_devices/translations/de.json +2 -1
- homeassistant/components/alexa_devices/translations/en.json +2 -1
- homeassistant/components/alexa_devices/translations/es.json +2 -1
- homeassistant/components/alexa_devices/translations/et.json +2 -1
- homeassistant/components/alexa_devices/translations/ga.json +11 -0
- homeassistant/components/alexa_devices/translations/lt.json +29 -2
- homeassistant/components/alexa_devices/translations/mk.json +8 -0
- homeassistant/components/alexa_devices/translations/sk.json +2 -1
- homeassistant/components/alexa_devices/translations/zh-Hant.json +2 -1
- homeassistant/components/androidtv_remote/translations/ko.json +1 -1
- homeassistant/components/aosmith/translations/ko.json +1 -1
- homeassistant/components/assist_satellite/translations/nl.json +7 -0
- homeassistant/components/aussie_broadband/translations/ko.json +1 -1
- homeassistant/components/bluetooth/manifest.json +1 -1
- homeassistant/components/bosch_shc/translations/ko.json +1 -1
- homeassistant/components/brunt/translations/ko.json +1 -1
- homeassistant/components/caldav/translations/ko.json +1 -1
- homeassistant/components/devolo_home_control/translations/lt.json +10 -0
- homeassistant/components/ecovacs/manifest.json +1 -1
- homeassistant/components/electric_kiwi/translations/ko.json +1 -1
- homeassistant/components/emoncms/translations/ga.json +1 -0
- homeassistant/components/enphase_envoy/translations/et.json +1 -0
- homeassistant/components/enphase_envoy/translations/lt.json +1 -0
- homeassistant/components/enphase_envoy/translations/nl.json +2 -1
- homeassistant/components/ezviz/translations/lt.json +10 -0
- homeassistant/components/fibaro/translations/ko.json +1 -1
- homeassistant/components/fitbit/translations/ko.json +1 -1
- homeassistant/components/frontend/manifest.json +1 -1
- homeassistant/components/geocaching/translations/ko.json +1 -1
- homeassistant/components/google/translations/ko.json +1 -1
- homeassistant/components/google_generative_ai_conversation/config_flow.py +5 -4
- homeassistant/components/google_generative_ai_conversation/translations/lt.json +28 -0
- homeassistant/components/google_photos/translations/ko.json +1 -1
- homeassistant/components/google_sheets/translations/ko.json +1 -1
- homeassistant/components/google_tasks/translations/ko.json +1 -1
- homeassistant/components/google_travel_time/translations/it.json +7 -0
- homeassistant/components/hassio/ingress.py +6 -2
- homeassistant/components/home_connect/translations/it.json +3 -0
- homeassistant/components/homeassistant/translations/it.json +24 -0
- homeassistant/components/homeassistant_hardware/translations/et.json +1 -0
- homeassistant/components/homeassistant_hardware/translations/ga.json +4 -0
- homeassistant/components/homeassistant_hardware/translations/lt.json +2 -0
- homeassistant/components/homeassistant_hardware/translations/mk.json +3 -0
- homeassistant/components/homeassistant_sky_connect/translations/et.json +2 -0
- homeassistant/components/homeassistant_sky_connect/translations/ga.json +8 -0
- homeassistant/components/homeassistant_sky_connect/translations/lt.json +4 -0
- homeassistant/components/homeassistant_yellow/translations/et.json +1 -0
- homeassistant/components/homeassistant_yellow/translations/ga.json +4 -0
- homeassistant/components/homeassistant_yellow/translations/lt.json +3 -0
- homeassistant/components/huawei_lte/translations/ko.json +1 -1
- homeassistant/components/icloud/translations/ko.json +1 -1
- homeassistant/components/immich/translations/nl.json +5 -0
- homeassistant/components/jellyfin/translations/ko.json +1 -1
- homeassistant/components/jvc_projector/translations/ko.json +1 -1
- homeassistant/components/laundrify/translations/ko.json +1 -1
- homeassistant/components/lcn/translations/cs.json +0 -3
- homeassistant/components/lcn/translations/de.json +1 -1
- homeassistant/components/lcn/translations/el.json +0 -3
- homeassistant/components/lcn/translations/en-GB.json +0 -3
- homeassistant/components/lcn/translations/en.json +1 -1
- homeassistant/components/lcn/translations/es.json +1 -1
- homeassistant/components/lcn/translations/et.json +0 -3
- homeassistant/components/lcn/translations/hu.json +0 -3
- homeassistant/components/lcn/translations/ja.json +0 -3
- homeassistant/components/lcn/translations/lt.json +0 -3
- homeassistant/components/lcn/translations/pt-BR.json +0 -3
- homeassistant/components/lcn/translations/pt.json +0 -3
- homeassistant/components/lcn/translations/ru.json +0 -3
- homeassistant/components/lcn/translations/sk.json +0 -3
- homeassistant/components/lcn/translations/sv.json +0 -3
- homeassistant/components/lcn/translations/tr.json +0 -3
- homeassistant/components/lcn/translations/zh-Hans.json +0 -3
- homeassistant/components/lcn/translations/zh-Hant.json +0 -3
- homeassistant/components/ld2410_ble/manifest.json +1 -1
- homeassistant/components/led_ble/manifest.json +1 -1
- homeassistant/components/lg_thinq/translations/et.json +3 -1
- homeassistant/components/lg_thinq/translations/lt.json +3 -1
- homeassistant/components/lidarr/translations/ko.json +1 -1
- homeassistant/components/litterrobot/translations/en-GB.json +1 -0
- homeassistant/components/litterrobot/translations/et.json +1 -0
- homeassistant/components/litterrobot/translations/ko.json +1 -1
- homeassistant/components/litterrobot/translations/lt.json +1 -0
- homeassistant/components/litterrobot/translations/sk.json +1 -0
- homeassistant/components/litterrobot/translations/zh-Hans.json +1 -0
- homeassistant/components/lyric/translations/it.json +3 -0
- homeassistant/components/lyric/translations/ko.json +1 -1
- homeassistant/components/melcloud/translations/ko.json +1 -1
- homeassistant/components/miele/translations/it.json +3 -0
- homeassistant/components/miele/translations/sk.json +1 -0
- homeassistant/components/mikrotik/translations/ko.json +1 -1
- homeassistant/components/mqtt/translations/bg.json +10 -0
- homeassistant/components/mqtt/translations/ga.json +10 -0
- homeassistant/components/mqtt/translations/it.json +7 -1
- homeassistant/components/mqtt/translations/lt.json +37 -0
- homeassistant/components/mqtt/translations/mk.json +9 -0
- homeassistant/components/nest/translations/ko.json +1 -1
- homeassistant/components/netatmo/translations/ko.json +1 -1
- homeassistant/components/nordpool/coordinator.py +3 -0
- homeassistant/components/notion/translations/ko.json +1 -1
- homeassistant/components/nuki/translations/ko.json +1 -1
- homeassistant/components/ollama/__init__.py +30 -4
- homeassistant/components/ollama/config_flow.py +164 -135
- homeassistant/components/ollama/conversation.py +8 -243
- homeassistant/components/ollama/entity.py +261 -0
- homeassistant/components/ollama/strings.json +12 -10
- homeassistant/components/ollama/translations/bg.json +0 -1
- homeassistant/components/ollama/translations/ca.json +0 -10
- homeassistant/components/ollama/translations/cs.json +1 -10
- homeassistant/components/ollama/translations/de.json +1 -10
- homeassistant/components/ollama/translations/el.json +1 -10
- homeassistant/components/ollama/translations/en-GB.json +0 -10
- homeassistant/components/ollama/translations/en.json +12 -10
- homeassistant/components/ollama/translations/es.json +1 -10
- homeassistant/components/ollama/translations/et.json +1 -10
- homeassistant/components/ollama/translations/fi.json +0 -7
- homeassistant/components/ollama/translations/fr.json +1 -10
- homeassistant/components/ollama/translations/ga.json +0 -3
- homeassistant/components/ollama/translations/hu.json +1 -10
- homeassistant/components/ollama/translations/it.json +0 -8
- homeassistant/components/ollama/translations/ja.json +1 -10
- homeassistant/components/ollama/translations/lt.json +1 -10
- homeassistant/components/ollama/translations/pt-BR.json +0 -3
- homeassistant/components/ollama/translations/pt.json +1 -10
- homeassistant/components/ollama/translations/ru.json +1 -10
- homeassistant/components/ollama/translations/sk.json +1 -10
- homeassistant/components/ollama/translations/sv.json +1 -10
- homeassistant/components/ollama/translations/tr.json +0 -10
- homeassistant/components/ollama/translations/zh-Hans.json +1 -10
- homeassistant/components/ollama/translations/zh-Hant.json +1 -10
- homeassistant/components/opentherm_gw/translations/bg.json +3 -0
- homeassistant/components/opentherm_gw/translations/et.json +3 -0
- homeassistant/components/opentherm_gw/translations/ga.json +3 -0
- homeassistant/components/opentherm_gw/translations/lt.json +3 -0
- homeassistant/components/opentherm_gw/translations/mk.json +7 -0
- homeassistant/components/openuv/translations/ko.json +1 -1
- homeassistant/components/opower/translations/ko.json +1 -1
- homeassistant/components/overkiz/translations/et.json +1 -0
- homeassistant/components/overkiz/translations/lt.json +1 -0
- homeassistant/components/overkiz/translations/zh-Hans.json +1 -0
- homeassistant/components/pegel_online/translations/lt.json +2 -1
- homeassistant/components/playstation_network/translations/cs.json +0 -3
- homeassistant/components/playstation_network/translations/el.json +0 -3
- homeassistant/components/playstation_network/translations/en.json +1 -1
- homeassistant/components/playstation_network/translations/et.json +1 -1
- homeassistant/components/playstation_network/translations/ga.json +0 -3
- homeassistant/components/playstation_network/translations/hu.json +0 -3
- homeassistant/components/playstation_network/translations/it.json +0 -3
- homeassistant/components/playstation_network/translations/lt.json +29 -0
- homeassistant/components/playstation_network/translations/mk.json +0 -3
- homeassistant/components/playstation_network/translations/nl.json +0 -3
- homeassistant/components/playstation_network/translations/pl.json +0 -3
- homeassistant/components/playstation_network/translations/pt.json +0 -3
- homeassistant/components/playstation_network/translations/sk.json +1 -1
- homeassistant/components/playstation_network/translations/sv.json +0 -3
- homeassistant/components/playstation_network/translations/zh-Hans.json +0 -3
- homeassistant/components/playstation_network/translations/zh-Hant.json +1 -1
- homeassistant/components/private_ble_device/manifest.json +1 -1
- homeassistant/components/proximity/translations/lt.json +25 -1
- homeassistant/components/proximity/translations/nl.json +3 -0
- homeassistant/components/pushover/translations/ko.json +1 -1
- homeassistant/components/radarr/translations/ko.json +1 -1
- homeassistant/components/renault/translations/ko.json +1 -1
- homeassistant/components/reolink/translations/it.json +3 -0
- homeassistant/components/reolink/translations/pl.json +2 -1
- homeassistant/components/ridwell/translations/ko.json +1 -1
- homeassistant/components/ring/translations/ko.json +1 -1
- homeassistant/components/roborock/translations/ko.json +1 -1
- homeassistant/components/russound_rio/translations/nl.json +7 -0
- homeassistant/components/schlage/translations/ko.json +1 -1
- homeassistant/components/scrape/translations/it.json +1 -0
- homeassistant/components/sense/translations/ko.json +1 -1
- homeassistant/components/sensibo/translations/pt-BR.json +4 -1
- homeassistant/components/sensibo/translations/sv.json +3 -3
- homeassistant/components/sensor/translations/it.json +1 -0
- homeassistant/components/shelly/__init__.py +8 -1
- homeassistant/components/shelly/const.py +3 -0
- homeassistant/components/shelly/repairs.py +87 -4
- homeassistant/components/shelly/strings.json +14 -0
- homeassistant/components/shelly/translations/bg.json +7 -0
- homeassistant/components/shelly/translations/de.json +14 -0
- homeassistant/components/shelly/translations/en.json +14 -0
- homeassistant/components/shelly/translations/es.json +14 -0
- homeassistant/components/shelly/translations/et.json +11 -0
- homeassistant/components/shelly/translations/ko.json +1 -1
- homeassistant/components/skybell/translations/ko.json +1 -1
- homeassistant/components/sleepiq/translations/ko.json +1 -1
- homeassistant/components/sma/config_flow.py +30 -1
- homeassistant/components/smarla/translations/it.json +9 -0
- homeassistant/components/smarttub/translations/ko.json +1 -1
- homeassistant/components/sonarr/translations/ko.json +1 -1
- homeassistant/components/sonos/translations/lt.json +9 -0
- homeassistant/components/spotify/translations/ko.json +1 -1
- homeassistant/components/sql/translations/it.json +1 -0
- homeassistant/components/squeezebox/translations/it.json +5 -0
- homeassistant/components/steam_online/translations/ko.json +1 -1
- homeassistant/components/subaru/translations/lt.json +2 -0
- homeassistant/components/subaru/translations/mk.json +12 -0
- homeassistant/components/surepetcare/translations/ko.json +1 -1
- homeassistant/components/switchbot/translations/en-GB.json +5 -0
- homeassistant/components/switchbot/translations/es.json +1 -0
- homeassistant/components/switchbot/translations/et.json +1 -0
- homeassistant/components/switchbot/translations/ga.json +13 -0
- homeassistant/components/switchbot/translations/lt.json +54 -0
- homeassistant/components/switchbot/translations/nl.json +1 -0
- homeassistant/components/switchbot/translations/sk.json +1 -0
- homeassistant/components/switchbot/translations/zh-Hans.json +1 -0
- homeassistant/components/switchbot/translations/zh-Hant.json +1 -0
- homeassistant/components/telegram_bot/translations/bg.json +10 -0
- homeassistant/components/telegram_bot/translations/ga.json +28 -0
- homeassistant/components/telegram_bot/translations/it.json +12 -0
- homeassistant/components/telegram_bot/translations/lt.json +30 -0
- homeassistant/components/telegram_bot/translations/mk.json +5 -0
- homeassistant/components/template/translations/it.json +1 -0
- homeassistant/components/tesla_fleet/translations/en-GB.json +3 -0
- homeassistant/components/tesla_fleet/translations/et.json +3 -0
- homeassistant/components/tesla_fleet/translations/lt.json +3 -0
- homeassistant/components/tesla_fleet/translations/mk.json +3 -0
- homeassistant/components/tesla_fleet/translations/nl.json +3 -0
- homeassistant/components/tesla_fleet/translations/zh-Hans.json +3 -0
- homeassistant/components/tessie/translations/ko.json +1 -1
- homeassistant/components/thermopro/manifest.json +1 -1
- homeassistant/components/thermopro/translations/lt.json +7 -0
- homeassistant/components/timer/translations/ko.json +2 -1
- homeassistant/components/totalconnect/translations/ko.json +1 -1
- homeassistant/components/transmission/translations/ko.json +1 -1
- homeassistant/components/tuya/translations/it.json +13 -0
- homeassistant/components/twitch/translations/ko.json +1 -1
- homeassistant/components/unifiprotect/config_flow.py +1 -1
- homeassistant/components/uptimerobot/translations/it.json +5 -0
- homeassistant/components/uptimerobot/translations/ko.json +1 -1
- homeassistant/components/watttime/translations/ko.json +1 -1
- homeassistant/components/weatherflow_cloud/translations/ga.json +1 -0
- homeassistant/components/webdav/translations/et.json +1 -0
- homeassistant/components/webdav/translations/ga.json +1 -0
- homeassistant/components/webdav/translations/lt.json +1 -0
- homeassistant/components/webdav/translations/mk.json +7 -0
- homeassistant/components/webdav/translations/zh-Hans.json +1 -0
- homeassistant/components/whirlpool/translations/et.json +1 -0
- homeassistant/components/whirlpool/translations/lt.json +1 -0
- homeassistant/components/whirlpool/translations/zh-Hans.json +1 -0
- homeassistant/components/withings/translations/it.json +3 -0
- homeassistant/components/withings/translations/ko.json +1 -1
- homeassistant/components/xiaomi_miio/translations/ko.json +1 -1
- homeassistant/components/yolink/translations/ko.json +1 -1
- homeassistant/components/youtube/translations/ko.json +1 -1
- homeassistant/components/zha/translations/it.json +10 -2
- homeassistant/components/zha/translations/lt.json +50 -0
- homeassistant/components/zwave_js/strings.json +11 -11
- homeassistant/components/zwave_js/translations/bg.json +6 -20
- homeassistant/components/zwave_js/translations/cs.json +0 -22
- homeassistant/components/zwave_js/translations/de.json +0 -23
- homeassistant/components/zwave_js/translations/el.json +0 -23
- homeassistant/components/zwave_js/translations/en-GB.json +0 -23
- homeassistant/components/zwave_js/translations/en.json +11 -11
- homeassistant/components/zwave_js/translations/es.json +0 -23
- homeassistant/components/zwave_js/translations/et.json +0 -23
- homeassistant/components/zwave_js/translations/fi.json +0 -11
- homeassistant/components/zwave_js/translations/ga.json +16 -22
- homeassistant/components/zwave_js/translations/hu.json +0 -23
- homeassistant/components/zwave_js/translations/it.json +0 -12
- homeassistant/components/zwave_js/translations/lt.json +32 -22
- homeassistant/components/zwave_js/translations/mk.json +16 -7
- homeassistant/components/zwave_js/translations/pt.json +0 -23
- homeassistant/components/zwave_js/translations/ru.json +0 -21
- homeassistant/components/zwave_js/translations/sk.json +0 -23
- homeassistant/components/zwave_js/translations/sv.json +0 -23
- homeassistant/components/zwave_js/translations/tr.json +0 -13
- homeassistant/components/zwave_js/translations/zh-Hans.json +0 -23
- homeassistant/components/zwave_js/translations/zh-Hant.json +0 -23
- homeassistant/const.py +1 -1
- homeassistant/package_constraints.txt +2 -2
- {homeassistant-2025.7.0b7.dist-info → homeassistant-2025.7.0b9.dist-info}/METADATA +1 -1
- {homeassistant-2025.7.0b7.dist-info → homeassistant-2025.7.0b9.dist-info}/RECORD +282 -277
- homeassistant/components/zwave_js/translations/sq.json +0 -7
- {homeassistant-2025.7.0b7.dist-info → homeassistant-2025.7.0b9.dist-info}/WHEEL +0 -0
- {homeassistant-2025.7.0b7.dist-info → homeassistant-2025.7.0b9.dist-info}/entry_points.txt +0 -0
- {homeassistant-2025.7.0b7.dist-info → homeassistant-2025.7.0b9.dist-info}/licenses/LICENSE.md +0 -0
- {homeassistant-2025.7.0b7.dist-info → homeassistant-2025.7.0b9.dist-info}/licenses/homeassistant/backports/LICENSE.Python +0 -0
- {homeassistant-2025.7.0b7.dist-info → homeassistant-2025.7.0b9.dist-info}/top_level.txt +0 -0
|
@@ -22,7 +22,7 @@ from homeassistant.config_entries import (
|
|
|
22
22
|
)
|
|
23
23
|
from homeassistant.const import CONF_LLM_HASS_API, CONF_NAME, CONF_URL
|
|
24
24
|
from homeassistant.core import HomeAssistant, callback
|
|
25
|
-
from homeassistant.helpers import llm
|
|
25
|
+
from homeassistant.helpers import config_validation as cv, llm
|
|
26
26
|
from homeassistant.helpers.selector import (
|
|
27
27
|
BooleanSelector,
|
|
28
28
|
NumberSelector,
|
|
@@ -38,6 +38,7 @@ from homeassistant.helpers.selector import (
|
|
|
38
38
|
)
|
|
39
39
|
from homeassistant.util.ssl import get_default_context
|
|
40
40
|
|
|
41
|
+
from . import OllamaConfigEntry
|
|
41
42
|
from .const import (
|
|
42
43
|
CONF_KEEP_ALIVE,
|
|
43
44
|
CONF_MAX_HISTORY,
|
|
@@ -72,43 +73,43 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
|
|
72
73
|
class OllamaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
73
74
|
"""Handle a config flow for Ollama."""
|
|
74
75
|
|
|
75
|
-
VERSION =
|
|
76
|
-
MINOR_VERSION =
|
|
76
|
+
VERSION = 3
|
|
77
|
+
MINOR_VERSION = 1
|
|
77
78
|
|
|
78
79
|
def __init__(self) -> None:
|
|
79
80
|
"""Initialize config flow."""
|
|
80
81
|
self.url: str | None = None
|
|
81
|
-
self.model: str | None = None
|
|
82
|
-
self.client: ollama.AsyncClient | None = None
|
|
83
|
-
self.download_task: asyncio.Task | None = None
|
|
84
82
|
|
|
85
83
|
async def async_step_user(
|
|
86
84
|
self, user_input: dict[str, Any] | None = None
|
|
87
85
|
) -> ConfigFlowResult:
|
|
88
86
|
"""Handle the initial step."""
|
|
89
|
-
|
|
90
|
-
self.url = user_input.get(CONF_URL, self.url)
|
|
91
|
-
self.model = user_input.get(CONF_MODEL, self.model)
|
|
92
|
-
|
|
93
|
-
if self.url is None:
|
|
87
|
+
if user_input is None:
|
|
94
88
|
return self.async_show_form(
|
|
95
|
-
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
|
89
|
+
step_id="user", data_schema=STEP_USER_DATA_SCHEMA
|
|
96
90
|
)
|
|
97
91
|
|
|
98
92
|
errors = {}
|
|
93
|
+
url = user_input[CONF_URL]
|
|
99
94
|
|
|
100
|
-
self._async_abort_entries_match({CONF_URL:
|
|
95
|
+
self._async_abort_entries_match({CONF_URL: url})
|
|
101
96
|
|
|
102
97
|
try:
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
url = cv.url(url)
|
|
99
|
+
except vol.Invalid:
|
|
100
|
+
errors["base"] = "invalid_url"
|
|
101
|
+
return self.async_show_form(
|
|
102
|
+
step_id="user",
|
|
103
|
+
data_schema=self.add_suggested_values_to_schema(
|
|
104
|
+
STEP_USER_DATA_SCHEMA, user_input
|
|
105
|
+
),
|
|
106
|
+
errors=errors,
|
|
105
107
|
)
|
|
106
|
-
async with asyncio.timeout(DEFAULT_TIMEOUT):
|
|
107
|
-
response = await self.client.list()
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
try:
|
|
110
|
+
client = ollama.AsyncClient(host=url, verify=get_default_context())
|
|
111
|
+
async with asyncio.timeout(DEFAULT_TIMEOUT):
|
|
112
|
+
await client.list()
|
|
112
113
|
except (TimeoutError, httpx.ConnectError):
|
|
113
114
|
errors["base"] = "cannot_connect"
|
|
114
115
|
except Exception:
|
|
@@ -117,10 +118,69 @@ class OllamaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
117
118
|
|
|
118
119
|
if errors:
|
|
119
120
|
return self.async_show_form(
|
|
120
|
-
step_id="user",
|
|
121
|
+
step_id="user",
|
|
122
|
+
data_schema=self.add_suggested_values_to_schema(
|
|
123
|
+
STEP_USER_DATA_SCHEMA, user_input
|
|
124
|
+
),
|
|
125
|
+
errors=errors,
|
|
121
126
|
)
|
|
122
127
|
|
|
123
|
-
|
|
128
|
+
return self.async_create_entry(
|
|
129
|
+
title=url,
|
|
130
|
+
data={CONF_URL: url},
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
@callback
|
|
135
|
+
def async_get_supported_subentry_types(
|
|
136
|
+
cls, config_entry: ConfigEntry
|
|
137
|
+
) -> dict[str, type[ConfigSubentryFlow]]:
|
|
138
|
+
"""Return subentries supported by this integration."""
|
|
139
|
+
return {"conversation": ConversationSubentryFlowHandler}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class ConversationSubentryFlowHandler(ConfigSubentryFlow):
|
|
143
|
+
"""Flow for managing conversation subentries."""
|
|
144
|
+
|
|
145
|
+
def __init__(self) -> None:
|
|
146
|
+
"""Initialize the subentry flow."""
|
|
147
|
+
super().__init__()
|
|
148
|
+
self._name: str | None = None
|
|
149
|
+
self._model: str | None = None
|
|
150
|
+
self.download_task: asyncio.Task | None = None
|
|
151
|
+
self._config_data: dict[str, Any] | None = None
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def _is_new(self) -> bool:
|
|
155
|
+
"""Return if this is a new subentry."""
|
|
156
|
+
return self.source == "user"
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def _client(self) -> ollama.AsyncClient:
|
|
160
|
+
"""Return the Ollama client."""
|
|
161
|
+
entry: OllamaConfigEntry = self._get_entry()
|
|
162
|
+
return entry.runtime_data
|
|
163
|
+
|
|
164
|
+
async def async_step_set_options(
|
|
165
|
+
self, user_input: dict[str, Any] | None = None
|
|
166
|
+
) -> SubentryFlowResult:
|
|
167
|
+
"""Handle model selection and configuration step."""
|
|
168
|
+
if self._get_entry().state != ConfigEntryState.LOADED:
|
|
169
|
+
return self.async_abort(reason="entry_not_loaded")
|
|
170
|
+
|
|
171
|
+
if user_input is None:
|
|
172
|
+
# Get available models from Ollama server
|
|
173
|
+
try:
|
|
174
|
+
async with asyncio.timeout(DEFAULT_TIMEOUT):
|
|
175
|
+
response = await self._client.list()
|
|
176
|
+
|
|
177
|
+
downloaded_models: set[str] = {
|
|
178
|
+
model_info["model"] for model_info in response.get("models", [])
|
|
179
|
+
}
|
|
180
|
+
except (TimeoutError, httpx.ConnectError, httpx.HTTPError):
|
|
181
|
+
_LOGGER.exception("Failed to get models from Ollama server")
|
|
182
|
+
return self.async_abort(reason="cannot_connect")
|
|
183
|
+
|
|
124
184
|
# Show models that have been downloaded first, followed by all known
|
|
125
185
|
# models (only latest tags).
|
|
126
186
|
models_to_list = [
|
|
@@ -131,52 +191,69 @@ class OllamaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
131
191
|
for m in sorted(MODEL_NAMES)
|
|
132
192
|
if m not in downloaded_models
|
|
133
193
|
]
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
SelectSelectorConfig(options=models_to_list, custom_value=True)
|
|
140
|
-
),
|
|
141
|
-
}
|
|
142
|
-
)
|
|
194
|
+
|
|
195
|
+
if self._is_new:
|
|
196
|
+
options = {}
|
|
197
|
+
else:
|
|
198
|
+
options = self._get_reconfigure_subentry().data.copy()
|
|
143
199
|
|
|
144
200
|
return self.async_show_form(
|
|
145
|
-
step_id="
|
|
146
|
-
data_schema=
|
|
201
|
+
step_id="set_options",
|
|
202
|
+
data_schema=vol.Schema(
|
|
203
|
+
ollama_config_option_schema(
|
|
204
|
+
self.hass, self._is_new, options, models_to_list
|
|
205
|
+
)
|
|
206
|
+
),
|
|
147
207
|
)
|
|
148
208
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
209
|
+
self._model = user_input[CONF_MODEL]
|
|
210
|
+
if self._is_new:
|
|
211
|
+
self._name = user_input.pop(CONF_NAME)
|
|
152
212
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
213
|
+
# Check if model needs to be downloaded
|
|
214
|
+
try:
|
|
215
|
+
async with asyncio.timeout(DEFAULT_TIMEOUT):
|
|
216
|
+
response = await self._client.list()
|
|
217
|
+
|
|
218
|
+
currently_downloaded_models: set[str] = {
|
|
219
|
+
model_info["model"] for model_info in response.get("models", [])
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if self._model not in currently_downloaded_models:
|
|
223
|
+
# Store the user input to use after download
|
|
224
|
+
self._config_data = user_input
|
|
225
|
+
# Ollama server needs to download model first
|
|
226
|
+
return await self.async_step_download()
|
|
227
|
+
except Exception:
|
|
228
|
+
_LOGGER.exception("Failed to check model availability")
|
|
229
|
+
return self.async_abort(reason="cannot_connect")
|
|
230
|
+
|
|
231
|
+
# Model is already downloaded, create/update the entry
|
|
232
|
+
if self._is_new:
|
|
233
|
+
return self.async_create_entry(
|
|
234
|
+
title=self._name,
|
|
235
|
+
data=user_input,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
return self.async_update_and_abort(
|
|
239
|
+
self._get_entry(),
|
|
240
|
+
self._get_reconfigure_subentry(),
|
|
241
|
+
data=user_input,
|
|
164
242
|
)
|
|
165
243
|
|
|
166
244
|
async def async_step_download(
|
|
167
245
|
self, user_input: dict[str, Any] | None = None
|
|
168
|
-
) ->
|
|
246
|
+
) -> SubentryFlowResult:
|
|
169
247
|
"""Step to wait for Ollama server to download a model."""
|
|
170
|
-
assert self.
|
|
171
|
-
assert self.client is not None
|
|
248
|
+
assert self._model is not None
|
|
172
249
|
|
|
173
250
|
if self.download_task is None:
|
|
174
251
|
# Tell Ollama server to pull the model.
|
|
175
252
|
# The task will block until the model and metadata are fully
|
|
176
253
|
# downloaded.
|
|
177
254
|
self.download_task = self.hass.async_create_background_task(
|
|
178
|
-
self.
|
|
179
|
-
f"Downloading {self.
|
|
255
|
+
self._client.pull(self._model),
|
|
256
|
+
f"Downloading {self._model}",
|
|
180
257
|
)
|
|
181
258
|
|
|
182
259
|
if self.download_task.done():
|
|
@@ -192,80 +269,28 @@ class OllamaConfigFlow(ConfigFlow, domain=DOMAIN):
|
|
|
192
269
|
progress_task=self.download_task,
|
|
193
270
|
)
|
|
194
271
|
|
|
195
|
-
async def async_step_finish(
|
|
196
|
-
self, user_input: dict[str, Any] | None = None
|
|
197
|
-
) -> ConfigFlowResult:
|
|
198
|
-
"""Step after model downloading has succeeded."""
|
|
199
|
-
assert self.url is not None
|
|
200
|
-
assert self.model is not None
|
|
201
|
-
|
|
202
|
-
return self.async_create_entry(
|
|
203
|
-
title=_get_title(self.model),
|
|
204
|
-
data={CONF_URL: self.url, CONF_MODEL: self.model},
|
|
205
|
-
subentries=[
|
|
206
|
-
{
|
|
207
|
-
"subentry_type": "conversation",
|
|
208
|
-
"data": {},
|
|
209
|
-
"title": _get_title(self.model),
|
|
210
|
-
"unique_id": None,
|
|
211
|
-
}
|
|
212
|
-
],
|
|
213
|
-
)
|
|
214
|
-
|
|
215
272
|
async def async_step_failed(
|
|
216
273
|
self, user_input: dict[str, Any] | None = None
|
|
217
|
-
) ->
|
|
274
|
+
) -> SubentryFlowResult:
|
|
218
275
|
"""Step after model downloading has failed."""
|
|
219
276
|
return self.async_abort(reason="download_failed")
|
|
220
277
|
|
|
221
|
-
|
|
222
|
-
@callback
|
|
223
|
-
def async_get_supported_subentry_types(
|
|
224
|
-
cls, config_entry: ConfigEntry
|
|
225
|
-
) -> dict[str, type[ConfigSubentryFlow]]:
|
|
226
|
-
"""Return subentries supported by this integration."""
|
|
227
|
-
return {"conversation": ConversationSubentryFlowHandler}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
class ConversationSubentryFlowHandler(ConfigSubentryFlow):
|
|
231
|
-
"""Flow for managing conversation subentries."""
|
|
232
|
-
|
|
233
|
-
@property
|
|
234
|
-
def _is_new(self) -> bool:
|
|
235
|
-
"""Return if this is a new subentry."""
|
|
236
|
-
return self.source == "user"
|
|
237
|
-
|
|
238
|
-
async def async_step_set_options(
|
|
278
|
+
async def async_step_finish(
|
|
239
279
|
self, user_input: dict[str, Any] | None = None
|
|
240
280
|
) -> SubentryFlowResult:
|
|
241
|
-
"""
|
|
242
|
-
|
|
243
|
-
if self._get_entry().state != ConfigEntryState.LOADED:
|
|
244
|
-
return self.async_abort(reason="entry_not_loaded")
|
|
245
|
-
|
|
246
|
-
errors: dict[str, str] = {}
|
|
247
|
-
|
|
248
|
-
if user_input is None:
|
|
249
|
-
if self._is_new:
|
|
250
|
-
options = {}
|
|
251
|
-
else:
|
|
252
|
-
options = self._get_reconfigure_subentry().data.copy()
|
|
281
|
+
"""Step after model downloading has succeeded."""
|
|
282
|
+
assert self._config_data is not None
|
|
253
283
|
|
|
254
|
-
|
|
284
|
+
# Model download completed, create/update the entry with stored config
|
|
285
|
+
if self._is_new:
|
|
255
286
|
return self.async_create_entry(
|
|
256
|
-
title=
|
|
257
|
-
data=
|
|
287
|
+
title=self._name,
|
|
288
|
+
data=self._config_data,
|
|
258
289
|
)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
data=user_input,
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
schema = ollama_config_option_schema(self.hass, self._is_new, options)
|
|
267
|
-
return self.async_show_form(
|
|
268
|
-
step_id="set_options", data_schema=vol.Schema(schema), errors=errors
|
|
290
|
+
return self.async_update_and_abort(
|
|
291
|
+
self._get_entry(),
|
|
292
|
+
self._get_reconfigure_subentry(),
|
|
293
|
+
data=self._config_data,
|
|
269
294
|
)
|
|
270
295
|
|
|
271
296
|
async_step_user = async_step_set_options
|
|
@@ -273,19 +298,14 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
|
|
|
273
298
|
|
|
274
299
|
|
|
275
300
|
def ollama_config_option_schema(
|
|
276
|
-
hass: HomeAssistant,
|
|
301
|
+
hass: HomeAssistant,
|
|
302
|
+
is_new: bool,
|
|
303
|
+
options: Mapping[str, Any],
|
|
304
|
+
models_to_list: list[SelectOptionDict],
|
|
277
305
|
) -> dict:
|
|
278
306
|
"""Ollama options schema."""
|
|
279
|
-
hass_apis: list[SelectOptionDict] = [
|
|
280
|
-
SelectOptionDict(
|
|
281
|
-
label=api.name,
|
|
282
|
-
value=api.id,
|
|
283
|
-
)
|
|
284
|
-
for api in llm.async_get_apis(hass)
|
|
285
|
-
]
|
|
286
|
-
|
|
287
307
|
if is_new:
|
|
288
|
-
schema: dict
|
|
308
|
+
schema: dict = {
|
|
289
309
|
vol.Required(CONF_NAME, default="Ollama Conversation"): str,
|
|
290
310
|
}
|
|
291
311
|
else:
|
|
@@ -293,6 +313,12 @@ def ollama_config_option_schema(
|
|
|
293
313
|
|
|
294
314
|
schema.update(
|
|
295
315
|
{
|
|
316
|
+
vol.Required(
|
|
317
|
+
CONF_MODEL,
|
|
318
|
+
description={"suggested_value": options.get(CONF_MODEL, DEFAULT_MODEL)},
|
|
319
|
+
): SelectSelector(
|
|
320
|
+
SelectSelectorConfig(options=models_to_list, custom_value=True)
|
|
321
|
+
),
|
|
296
322
|
vol.Optional(
|
|
297
323
|
CONF_PROMPT,
|
|
298
324
|
description={
|
|
@@ -304,7 +330,18 @@ def ollama_config_option_schema(
|
|
|
304
330
|
vol.Optional(
|
|
305
331
|
CONF_LLM_HASS_API,
|
|
306
332
|
description={"suggested_value": options.get(CONF_LLM_HASS_API)},
|
|
307
|
-
): SelectSelector(
|
|
333
|
+
): SelectSelector(
|
|
334
|
+
SelectSelectorConfig(
|
|
335
|
+
options=[
|
|
336
|
+
SelectOptionDict(
|
|
337
|
+
label=api.name,
|
|
338
|
+
value=api.id,
|
|
339
|
+
)
|
|
340
|
+
for api in llm.async_get_apis(hass)
|
|
341
|
+
],
|
|
342
|
+
multiple=True,
|
|
343
|
+
)
|
|
344
|
+
),
|
|
308
345
|
vol.Optional(
|
|
309
346
|
CONF_NUM_CTX,
|
|
310
347
|
description={
|
|
@@ -350,11 +387,3 @@ def ollama_config_option_schema(
|
|
|
350
387
|
)
|
|
351
388
|
|
|
352
389
|
return schema
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
def _get_title(model: str) -> str:
|
|
356
|
-
"""Get title for config entry."""
|
|
357
|
-
if model.endswith(":latest"):
|
|
358
|
-
model = model.split(":", maxsplit=1)[0]
|
|
359
|
-
|
|
360
|
-
return model
|