Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions homeassistant/components/ai_task/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
schema=vol.Schema(
{
vol.Required(ATTR_TASK_NAME): cv.string,
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
vol.Optional(ATTR_ENTITY_ID): cv.entity_id,
vol.Required(ATTR_INSTRUCTIONS): cv.string,
vol.Optional(ATTR_ATTACHMENTS): vol.All(
cv.ensure_list, [selector.MediaSelector({"accept": ["*/*"]})]
Expand Down Expand Up @@ -163,9 +163,10 @@ async def async_service_generate_image(call: ServiceCall) -> ServiceResponse:
class AITaskPreferences:
"""AI Task preferences."""

KEYS = ("gen_data_entity_id",)
KEYS = ("gen_data_entity_id", "gen_image_entity_id")

gen_data_entity_id: str | None = None
gen_image_entity_id: str | None = None

def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the preferences."""
Expand All @@ -179,17 +180,21 @@ async def async_load(self) -> None:
if data is None:
return
for key in self.KEYS:
setattr(self, key, data[key])
setattr(self, key, data.get(key))

@callback
def async_set_preferences(
self,
*,
gen_data_entity_id: str | None | UndefinedType = UNDEFINED,
gen_image_entity_id: str | None | UndefinedType = UNDEFINED,
) -> None:
"""Set the preferences."""
changed = False
for key, value in (("gen_data_entity_id", gen_data_entity_id),):
for key, value in (
("gen_data_entity_id", gen_data_entity_id),
("gen_image_entity_id", gen_image_entity_id),
):
if value is not UNDEFINED:
if getattr(self, key) != value:
setattr(self, key, value)
Expand Down
5 changes: 5 additions & 0 deletions homeassistant/components/ai_task/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ async def _async_get_ai_task_chat_log(
task: GenDataTask | GenImageTask,
) -> AsyncGenerator[ChatLog]:
"""Context manager used to manage the ChatLog used during an AI Task."""
user_llm_hass_api: llm.API | None = None
if isinstance(task, GenDataTask):
user_llm_hass_api = task.llm_api

# pylint: disable-next=contextmanager-generator-missing-cleanup
with (
async_get_chat_log(
Expand All @@ -77,6 +81,7 @@ async def _async_get_ai_task_chat_log(
device_id=None,
),
user_llm_prompt=DEFAULT_SYSTEM_PROMPT,
user_llm_hass_api=user_llm_hass_api,
)

chat_log.async_add_user_content(
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/ai_task/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def websocket_get_preferences(
{
vol.Required("type"): "ai_task/preferences/set",
vol.Optional("gen_data_entity_id"): vol.Any(str, None),
vol.Optional("gen_image_entity_id"): vol.Any(str, None),
}
)
@websocket_api.require_admin
Expand Down
14 changes: 13 additions & 1 deletion homeassistant/components/ai_task/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from homeassistant.components.http.auth import async_sign_path
from homeassistant.core import HomeAssistant, ServiceResponse, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import llm
from homeassistant.helpers.chat_session import ChatSession, async_get_chat_session
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.network import get_url
Expand Down Expand Up @@ -116,6 +117,7 @@ async def async_generate_data(
instructions: str,
structure: vol.Schema | None = None,
attachments: list[dict] | None = None,
llm_api: llm.API | None = None,
) -> GenDataTaskResult:
"""Run a data generation task in the AI Task integration."""
if entity_id is None:
Expand Down Expand Up @@ -151,6 +153,7 @@ async def async_generate_data(
instructions=instructions,
structure=structure,
attachments=resolved_attachments or None,
llm_api=llm_api,
),
)

Expand All @@ -177,11 +180,17 @@ async def async_generate_image(
hass: HomeAssistant,
*,
task_name: str,
entity_id: str,
entity_id: str | None = None,
instructions: str,
attachments: list[dict] | None = None,
) -> ServiceResponse:
"""Run an image generation task in the AI Task integration."""
if entity_id is None:
entity_id = hass.data[DATA_PREFERENCES].gen_image_entity_id

if entity_id is None:
raise HomeAssistantError("No entity_id provided and no preferred entity set")

entity = hass.data[DATA_COMPONENT].get_entity(entity_id)
if entity is None:
raise HomeAssistantError(f"AI Task entity {entity_id} not found")
Expand Down Expand Up @@ -266,6 +275,9 @@ class GenDataTask:
attachments: list[conversation.Attachment] | None = None
"""List of attachments to go along the instructions."""

llm_api: llm.API | None = None
"""API to provide to the LLM."""

def __str__(self) -> str:
"""Return task as a string."""
return f"<GenDataTask {self.name}: {id(self)}>"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/airzone/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
"documentation": "https://www.home-assistant.io/integrations/airzone",
"iot_class": "local_polling",
"loggers": ["aioairzone"],
"requirements": ["aioairzone==1.0.0"]
"requirements": ["aioairzone==1.0.1"]
}
24 changes: 20 additions & 4 deletions homeassistant/components/alexa_devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers.typing import ConfigType

from .const import _LOGGER, CONF_LOGIN_DATA, COUNTRY_DOMAINS, DOMAIN
from .const import _LOGGER, CONF_LOGIN_DATA, CONF_SITE, COUNTRY_DOMAINS, DOMAIN
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
from .services import async_setup_services

Expand Down Expand Up @@ -42,7 +42,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo

async def async_migrate_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Migrate old entry."""
if entry.version == 1 and entry.minor_version == 1:

if entry.version == 1 and entry.minor_version < 3:
if CONF_SITE in entry.data:
# Site in data (wrong place), just move to login data
new_data = entry.data.copy()
new_data[CONF_LOGIN_DATA][CONF_SITE] = new_data[CONF_SITE]
new_data.pop(CONF_SITE)
hass.config_entries.async_update_entry(
entry, data=new_data, version=1, minor_version=3
)
return True

if CONF_SITE in entry.data[CONF_LOGIN_DATA]:
# Site is there, just update version to avoid future migrations
hass.config_entries.async_update_entry(entry, version=1, minor_version=3)
return True

_LOGGER.debug(
"Migrating from version %s.%s", entry.version, entry.minor_version
)
Expand All @@ -53,10 +69,10 @@ async def async_migrate_entry(hass: HomeAssistant, entry: AmazonConfigEntry) ->

# Add site to login data
new_data = entry.data.copy()
new_data[CONF_LOGIN_DATA]["site"] = f"https://www.amazon.{domain}"
new_data[CONF_LOGIN_DATA][CONF_SITE] = f"https://www.amazon.{domain}"

hass.config_entries.async_update_entry(
entry, data=new_data, version=1, minor_version=2
entry, data=new_data, version=1, minor_version=3
)

_LOGGER.info(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/alexa_devices/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Alexa Devices."""

VERSION = 1
MINOR_VERSION = 2
MINOR_VERSION = 3

async def async_step_user(
self, user_input: dict[str, Any] | None = None
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/alexa_devices/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

DOMAIN = "alexa_devices"
CONF_LOGIN_DATA = "login_data"
CONF_SITE = "site"

DEFAULT_DOMAIN = "com"
COUNTRY_DOMAINS = {
Expand Down
6 changes: 4 additions & 2 deletions homeassistant/components/androidtv_remote/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

_LOGGER = logging.getLogger(__name__)

APPS_NEW_ID = "NewApp"
APPS_NEW_ID = "add_new"
CONF_APP_DELETE = "app_delete"
CONF_APP_ID = "app_id"

Expand Down Expand Up @@ -287,7 +287,9 @@ async def async_step_init(
{
vol.Optional(CONF_APPS): SelectSelector(
SelectSelectorConfig(
options=apps, mode=SelectSelectorMode.DROPDOWN
options=apps,
mode=SelectSelectorMode.DROPDOWN,
translation_key="apps",
)
),
vol.Required(
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/androidtv_remote/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["androidtvremote2"],
"quality_scale": "platinum",
"requirements": ["androidtvremote2==0.2.3"],
"zeroconf": ["_androidtvremote2._tcp.local."]
}
78 changes: 78 additions & 0 deletions homeassistant/components/androidtv_remote/quality_scale.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
rules:
# Bronze
action-setup:
status: exempt
comment: No integration-specific service actions are defined.
appropriate-polling:
status: exempt
comment: This is a push-based integration.
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions: done
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
entity-event-setup: done
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: done
test-before-setup: done
unique-config-entry: done

# Silver
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: done
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates: done
reauthentication-flow: done
test-coverage: done

# Gold
devices: done
diagnostics: done
discovery-update-info: done
discovery: done
docs-data-update: done
docs-examples: done
docs-known-limitations: done
docs-supported-devices: done
docs-supported-functions: done
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices:
status: exempt
comment: The integration is configured on a per-device basis, so there are no dynamic devices to add.
entity-category:
status: exempt
comment: All entities are primary and do not require a specific category.
entity-device-class: done
entity-disabled-by-default:
status: exempt
comment: The integration provides only primary entities that should be enabled.
entity-translations: done
exception-translations: done
icon-translations:
status: exempt
comment: Icons are provided by the entity's device class, and no state-based icons are needed.
reconfiguration-flow: done
repair-issues:
status: exempt
comment: The integration uses the reauth flow for authentication issues, and no other repairable issues have been identified.
stale-devices:
status: exempt
comment: The integration manages a single device per config entry. Stale device removal is handled by removing the config entry.

# Platinum
async-dependency: done
inject-websession:
status: exempt
comment: The underlying library does not use HTTP for communication.
strict-typing: done
7 changes: 7 additions & 0 deletions homeassistant/components/androidtv_remote/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,12 @@
"invalid_media_type": {
"message": "Invalid media type: {media_type}"
}
},
"selector": {
"apps": {
"options": {
"add_new": "Add new"
}
}
}
}
2 changes: 1 addition & 1 deletion homeassistant/components/bluetooth/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
"bluetooth-auto-recovery==1.5.2",
"bluetooth-data-tools==1.28.2",
"dbus-fast==2.44.3",
"habluetooth==5.6.0"
"habluetooth==5.6.2"
]
}
8 changes: 6 additions & 2 deletions homeassistant/components/conversation/chat_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,14 +507,18 @@ async def async_update_llm_data(
async def async_provide_llm_data(
self,
llm_context: llm.LLMContext,
user_llm_hass_api: str | list[str] | None = None,
user_llm_hass_api: str | list[str] | llm.API | None = None,
user_llm_prompt: str | None = None,
user_extra_system_prompt: str | None = None,
) -> None:
"""Set the LLM system prompt."""
llm_api: llm.APIInstance | None = None

if user_llm_hass_api:
if user_llm_hass_api is None:
pass
elif isinstance(user_llm_hass_api, llm.API):
llm_api = await user_llm_hass_api.async_get_api_instance(llm_context)
else:
try:
llm_api = await llm.async_get_api(
self.hass,
Expand Down
11 changes: 11 additions & 0 deletions homeassistant/components/ecowitt/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,17 @@
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.PM1: SensorEntityDescription(
key="PM1",
device_class=SensorDeviceClass.PM1,
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
EcoWittSensorTypes.PM4: SensorEntityDescription(
key="PM4",
native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
}


Expand Down
Loading
Loading