Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions CODEOWNERS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion homeassistant/components/august/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@
"documentation": "https://www.home-assistant.io/integrations/august",
"iot_class": "cloud_push",
"loggers": ["pubnub", "yalexs"],
"requirements": ["yalexs==9.1.0", "yalexs-ble==3.1.2"]
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.1.2"]
}
25 changes: 11 additions & 14 deletions homeassistant/components/conversation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,13 @@
ATTR_LANGUAGE,
ATTR_TEXT,
DATA_COMPONENT,
DATA_DEFAULT_ENTITY,
DOMAIN,
HOME_ASSISTANT_AGENT,
SERVICE_PROCESS,
SERVICE_RELOAD,
ConversationEntityFeature,
)
from .default_agent import DefaultAgent, async_setup_default_agent
from .default_agent import async_setup_default_agent
from .entity import ConversationEntity
from .http import async_setup as async_setup_conversation_http
from .models import AbstractConversationAgent, ConversationInput, ConversationResult
Expand Down Expand Up @@ -142,7 +141,7 @@ def async_unset_agent(
hass: HomeAssistant,
config_entry: ConfigEntry,
) -> None:
"""Set the agent to handle the conversations."""
"""Unset the agent to handle the conversations."""
get_agent_manager(hass).async_unset_agent(config_entry.entry_id)


Expand Down Expand Up @@ -241,10 +240,10 @@ async def async_handle_sentence_triggers(

Returns None if no match occurred.
"""
default_agent = async_get_agent(hass)
assert isinstance(default_agent, DefaultAgent)
agent = get_agent_manager(hass).default_agent
assert agent is not None

return await default_agent.async_handle_sentence_triggers(user_input)
return await agent.async_handle_sentence_triggers(user_input)


async def async_handle_intents(
Expand All @@ -257,12 +256,10 @@ async def async_handle_intents(

Returns None if no match occurred.
"""
default_agent = async_get_agent(hass)
assert isinstance(default_agent, DefaultAgent)
agent = get_agent_manager(hass).default_agent
assert agent is not None

return await default_agent.async_handle_intents(
user_input, intent_filter=intent_filter
)
return await agent.async_handle_intents(user_input, intent_filter=intent_filter)


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
Expand Down Expand Up @@ -298,9 +295,9 @@ async def handle_process(service: ServiceCall) -> ServiceResponse:

async def handle_reload(service: ServiceCall) -> None:
"""Reload intents."""
await hass.data[DATA_DEFAULT_ENTITY].async_reload(
language=service.data.get(ATTR_LANGUAGE)
)
agent = get_agent_manager(hass).default_agent
if agent is not None:
await agent.async_reload(language=service.data.get(ATTR_LANGUAGE))

hass.services.async_register(
DOMAIN,
Expand Down
16 changes: 13 additions & 3 deletions homeassistant/components/conversation/agent_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

import dataclasses
import logging
from typing import Any
from typing import TYPE_CHECKING, Any

import voluptuous as vol

from homeassistant.core import Context, HomeAssistant, async_get_hass, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv, intent, singleton

from .const import DATA_COMPONENT, DATA_DEFAULT_ENTITY, HOME_ASSISTANT_AGENT
from .const import DATA_COMPONENT, HOME_ASSISTANT_AGENT
from .entity import ConversationEntity
from .models import (
AbstractConversationAgent,
Expand All @@ -28,6 +28,9 @@

_LOGGER = logging.getLogger(__name__)

if TYPE_CHECKING:
from .default_agent import DefaultAgent


@singleton.singleton("conversation_agent")
@callback
Expand All @@ -49,8 +52,10 @@ def async_get_agent(
hass: HomeAssistant, agent_id: str | None = None
) -> AbstractConversationAgent | ConversationEntity | None:
"""Get specified agent."""
manager = get_agent_manager(hass)

if agent_id is None or agent_id == HOME_ASSISTANT_AGENT:
return hass.data[DATA_DEFAULT_ENTITY]
return manager.default_agent

if "." in agent_id:
return hass.data[DATA_COMPONENT].get_entity(agent_id)
Expand Down Expand Up @@ -134,6 +139,7 @@ def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the conversation agents."""
self.hass = hass
self._agents: dict[str, AbstractConversationAgent] = {}
self.default_agent: DefaultAgent | None = None

@callback
def async_get_agent(self, agent_id: str) -> AbstractConversationAgent | None:
Expand Down Expand Up @@ -182,3 +188,7 @@ def async_set_agent(self, agent_id: str, agent: AbstractConversationAgent) -> No
def async_unset_agent(self, agent_id: str) -> None:
"""Unset the agent."""
self._agents.pop(agent_id, None)

async def async_setup_default_agent(self, agent: DefaultAgent) -> None:
"""Set up the default agent."""
self.default_agent = agent
3 changes: 0 additions & 3 deletions homeassistant/components/conversation/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@
if TYPE_CHECKING:
from homeassistant.helpers.entity_component import EntityComponent

from .default_agent import DefaultAgent
from .entity import ConversationEntity

DOMAIN = "conversation"
DEFAULT_EXPOSED_ATTRIBUTES = {"device_class"}
HOME_ASSISTANT_AGENT = "conversation.home_assistant"

ATTR_TEXT = "text"
Expand All @@ -26,7 +24,6 @@
SERVICE_RELOAD = "reload"

DATA_COMPONENT: HassKey[EntityComponent[ConversationEntity]] = HassKey(DOMAIN)
DATA_DEFAULT_ENTITY: HassKey[DefaultAgent] = HassKey(f"{DOMAIN}_default_entity")


class ConversationEntityFeature(IntFlag):
Expand Down
18 changes: 8 additions & 10 deletions homeassistant/components/conversation/default_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,9 @@
from homeassistant.util import language as language_util
from homeassistant.util.json import JsonObjectType, json_loads_object

from .agent_manager import get_agent_manager
from .chat_log import AssistantContent, ChatLog
from .const import (
DATA_DEFAULT_ENTITY,
DEFAULT_EXPOSED_ATTRIBUTES,
DOMAIN,
ConversationEntityFeature,
)
from .const import DOMAIN, ConversationEntityFeature
from .entity import ConversationEntity
from .models import ConversationInput, ConversationResult
from .trace import ConversationTraceEventType, async_conversation_trace_append
Expand All @@ -83,6 +79,8 @@
_DEFAULT_ERROR_TEXT = "Sorry, I couldn't understand that"
_ENTITY_REGISTRY_UPDATE_FIELDS = ["aliases", "name", "original_name"]

_DEFAULT_EXPOSED_ATTRIBUTES = {"device_class"}

REGEX_TYPE = type(re.compile(""))
TRIGGER_CALLBACK_TYPE = Callable[
[ConversationInput, RecognizeResult], Awaitable[str | None]
Expand Down Expand Up @@ -209,9 +207,9 @@ async def async_setup_default_agent(
config_intents: dict[str, Any],
) -> None:
"""Set up entity registry listener for the default agent."""
entity = DefaultAgent(hass, config_intents)
await entity_component.async_add_entities([entity])
hass.data[DATA_DEFAULT_ENTITY] = entity
agent = DefaultAgent(hass, config_intents)
await entity_component.async_add_entities([agent])
await get_agent_manager(hass).async_setup_default_agent(agent)

@core.callback
def async_entity_state_listener(
Expand Down Expand Up @@ -846,7 +844,7 @@ def _get_entity_name_tuples(
context = {"domain": state.domain}
if state.attributes:
# Include some attributes
for attr in DEFAULT_EXPOSED_ATTRIBUTES:
for attr in _DEFAULT_EXPOSED_ATTRIBUTES:
if attr not in state.attributes:
continue
context[attr] = state.attributes[attr]
Expand Down
8 changes: 5 additions & 3 deletions homeassistant/components/conversation/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
async_get_agent,
get_agent_manager,
)
from .const import DATA_COMPONENT, DATA_DEFAULT_ENTITY
from .const import DATA_COMPONENT
from .default_agent import (
METADATA_CUSTOM_FILE,
METADATA_CUSTOM_SENTENCE,
Expand Down Expand Up @@ -169,7 +169,8 @@ async def websocket_list_sentences(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
) -> None:
"""List custom registered sentences."""
agent = hass.data[DATA_DEFAULT_ENTITY]
agent = get_agent_manager(hass).default_agent
assert agent is not None

sentences = []
for trigger_data in agent.trigger_sentences:
Expand All @@ -191,7 +192,8 @@ async def websocket_hass_agent_debug(
hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict
) -> None:
"""Return intents that would be matched by the default agent for a list of sentences."""
agent = hass.data[DATA_DEFAULT_ENTITY]
agent = get_agent_manager(hass).default_agent
assert agent is not None

# Return results for each sentence in the same order as the input.
result_dicts: list[dict[str, Any] | None] = []
Expand Down
7 changes: 5 additions & 2 deletions homeassistant/components/conversation/trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import UNDEFINED, ConfigType

from .const import DATA_DEFAULT_ENTITY, DOMAIN
from .agent_manager import get_agent_manager
from .const import DOMAIN
from .models import ConversationInput


Expand Down Expand Up @@ -123,4 +124,6 @@ async def call_action(
# two trigger copies for who will provide a response.
return None

return hass.data[DATA_DEFAULT_ENTITY].register_trigger(sentences, call_action)
agent = get_agent_manager(hass).default_agent
assert agent is not None
return agent.register_trigger(sentences, call_action)
9 changes: 3 additions & 6 deletions homeassistant/components/miele/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,18 +338,17 @@ class MieleActions(IntEnum):
}


class StateProgramType(MieleEnum):
class StateProgramType(MieleEnum, missing_to_none=True):
"""Defines program types."""

normal_operation_mode = 0
own_program = 1
automatic_program = 2
cleaning_care_program = 3
maintenance_program = 4
missing2none = -9999


class StateDryingStep(MieleEnum):
class StateDryingStep(MieleEnum, missing_to_none=True):
"""Defines drying steps."""

extra_dry = 0
Expand All @@ -360,7 +359,6 @@ class StateDryingStep(MieleEnum):
hand_iron_2 = 5
machine_iron = 6
smoothing = 7
missing2none = -9999


WASHING_MACHINE_PROGRAM_ID: dict[int, str] = {
Expand Down Expand Up @@ -1314,7 +1312,7 @@ class StateDryingStep(MieleEnum):
}


class PlatePowerStep(MieleEnum):
class PlatePowerStep(MieleEnum, missing_to_none=True):
"""Plate power settings."""

plate_step_0 = 0
Expand All @@ -1339,4 +1337,3 @@ class PlatePowerStep(MieleEnum):
plate_step_18 = 18
plate_step_boost = 117, 118, 218
plate_step_boost_2 = 217
missing2none = -9999
2 changes: 1 addition & 1 deletion homeassistant/components/miele/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"iot_class": "cloud_push",
"loggers": ["pymiele"],
"quality_scale": "platinum",
"requirements": ["pymiele==0.5.4"],
"requirements": ["pymiele==0.5.5"],
"single_config_entry": true,
"zeroconf": ["_mieleathome._tcp.local."]
}
3 changes: 1 addition & 2 deletions homeassistant/components/miele/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class FanProgram(IntEnum):
}


class MieleVacuumStateCode(MieleEnum):
class MieleVacuumStateCode(MieleEnum, missing_to_none=True):
"""Define vacuum state codes."""

idle = 0
Expand All @@ -82,7 +82,6 @@ class MieleVacuumStateCode(MieleEnum):
blocked_front_wheel = 5900
docked = 5903, 5904
remote_controlled = 5910
missing2none = -9999


SUPPORTED_FEATURES = (
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/ntfy/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"iot_class": "cloud_push",
"loggers": ["aionfty"],
"quality_scale": "bronze",
"requirements": ["aiontfy==0.5.5"]
"requirements": ["aiontfy==0.6.0"]
}
6 changes: 5 additions & 1 deletion homeassistant/components/number/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,11 @@ def native_unit_of_measurement(self) -> str | None:
@final
@property
def __native_unit_of_measurement_compat(self) -> str | None:
"""Process ambiguous units."""
"""Handle wrong character coding in unit provided by integrations.

NumberEntity should read the number's native unit through this property instead
of through native_unit_of_measurement.
"""
native_unit_of_measurement = self.native_unit_of_measurement
return AMBIGUOUS_UNITS.get(
native_unit_of_measurement, native_unit_of_measurement
Expand Down
8 changes: 6 additions & 2 deletions homeassistant/components/sensor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ def _is_valid_suggested_unit(self, suggested_unit_of_measurement: str) -> bool:
because a unit converter supports both.
"""
# No need to check the unit converter if the units are the same
if self.native_unit_of_measurement == suggested_unit_of_measurement:
if self.__native_unit_of_measurement_compat == suggested_unit_of_measurement:
return True

# Make sure there is a unit converter and it supports both units
Expand Down Expand Up @@ -478,7 +478,11 @@ def native_unit_of_measurement(self) -> str | None:
@final
@property
def __native_unit_of_measurement_compat(self) -> str | None:
"""Process ambiguous units."""
"""Handle wrong character coding in unit provided by integrations.

SensorEntity should read the sensor's native unit through this property instead
of through native_unit_of_measurement.
"""
native_unit_of_measurement = self.native_unit_of_measurement
return AMBIGUOUS_UNITS.get(
native_unit_of_measurement,
Expand Down
9 changes: 7 additions & 2 deletions homeassistant/components/switchbot_cloud/manifest.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
{
"domain": "switchbot_cloud",
"name": "SwitchBot Cloud",
"codeowners": ["@SeraphicRav", "@laurence-presland", "@Gigatrappeur"],
"codeowners": [
"@SeraphicRav",
"@laurence-presland",
"@Gigatrappeur",
"@XiaoLing-git"
],
"config_flow": true,
"dependencies": ["webhook"],
"documentation": "https://www.home-assistant.io/integrations/switchbot_cloud",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["switchbot_api"],
"requirements": ["switchbot-api==2.7.0"]
"requirements": ["switchbot-api==2.8.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/yale/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
"documentation": "https://www.home-assistant.io/integrations/yale",
"iot_class": "cloud_push",
"loggers": ["socketio", "engineio", "yalexs"],
"requirements": ["yalexs==9.1.0", "yalexs-ble==3.1.2"]
"requirements": ["yalexs==9.2.0", "yalexs-ble==3.1.2"]
}
Loading
Loading