Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
06233b5
Bump aioesphomeapi to 37.1.5 (#149656)
bdraco Jul 30, 2025
03ee97d
Clarify description of `turn_away_mode_on.osoenergy` action (#149655)
NoRi2909 Jul 30, 2025
ac86f2e
Add Frient brand (#149654)
edenhaus Jul 30, 2025
a79d2da
Move group toggle descriptions to data_description (#149625)
karwosts Jul 30, 2025
15e45df
Use async_create_clientsession in Alexa Devices (#149432)
chemelli74 Jul 30, 2025
5930ac6
Use translation_placeholders in tuya switch descriptions (#149664)
epenet Jul 30, 2025
1eb6d5f
Add action for set_program_oven to miele (#149620)
astrandb Jul 30, 2025
828f979
Use Tuya device listener in binary sensor tests (#148890)
epenet Jul 30, 2025
749fc31
Validate selectors in the trigger helper (#149662)
arturpragacz Jul 30, 2025
6c2a662
Add config flow to template cover platform (#149433)
Petro31 Jul 30, 2025
1a75a88
Add actions to Alexa Devices (#145645)
chemelli74 Jul 30, 2025
69e3a5b
Add support for more switchbot cloud vacuum models (#146637)
XiaoLing-git Jul 30, 2025
260ca70
Add Light platform to Switchbot cloud (#146382)
XiaoLing-git Jul 30, 2025
25169e9
Bump datadogpy to 0.52.0 (#149596)
avedor Jul 30, 2025
d8016f7
Remove stale devices in Uptime Kuma (#149605)
tr4nt0r Jul 30, 2025
779f0af
Refactor Habitica button and switch functions to use habiticalib inst…
tr4nt0r Jul 30, 2025
dd0b23a
husqvarna_automower_ble: Support battery percentage sensor (#146159)
alistair23 Jul 30, 2025
ba4e7e5
Add friend tracking to PlayStation Network (#149546)
tr4nt0r Jul 30, 2025
c4d4ef8
Add hassio discovery flow to Uptime Kuma (#148770)
tr4nt0r Jul 30, 2025
a5b075a
Add climate support for MQTT subentries (#149451)
jbouwh Jul 30, 2025
70cfdfa
Remove unnecessary CONFIG_SCHEMA from Uptime Kuma integration (#149601)
tr4nt0r Jul 30, 2025
a21af78
Add config flow to template light platform (#149448)
Petro31 Jul 30, 2025
91be25a
Add get recipes search service to Mealie integration (#149348)
lucasfijen Jul 30, 2025
99ee56a
Add Precipitation sensors to Weatherflow Cloud (#149619)
jeeftor Jul 30, 2025
223c340
Add missing colons in miele messages (#149668)
astrandb Jul 30, 2025
1b58809
Add AI Task to OpenRouter (#149275)
joostlek Jul 30, 2025
fc900a6
Revert logging for unsupported Tuya devices (#149665)
epenet Jul 30, 2025
160b61e
Add config flow to template fan platform (#149446)
Petro31 Jul 30, 2025
daea76c
Update frontend to 20250730.0 (#149672)
bramkragten Jul 30, 2025
edca3fc
Add matter to Third Reality (#149659)
edenhaus Jul 30, 2025
d481a69
Add config flow to template vacuum platform (#149458)
Petro31 Jul 30, 2025
6306baa
Add config flow to template lock platform (#149449)
Petro31 Jul 30, 2025
8193259
Revert "Add select for heating circuit to Tado zones" (#149670)
joostlek Jul 30, 2025
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
5 changes: 5 additions & 0 deletions homeassistant/brands/frient.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"domain": "frient",
"name": "Frient",
"iot_standards": ["zigbee"]
}
2 changes: 1 addition & 1 deletion homeassistant/brands/third_reality.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"domain": "third_reality",
"name": "Third Reality",
"iot_standards": ["zigbee"]
"iot_standards": ["matter", "zigbee"]
}
21 changes: 15 additions & 6 deletions homeassistant/components/alexa_devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client, config_validation as cv
from homeassistant.helpers.typing import ConfigType

from .const import DOMAIN
from .coordinator import AmazonConfigEntry, AmazonDevicesCoordinator
from .services import async_setup_services

PLATFORMS = [
Platform.BINARY_SENSOR,
Expand All @@ -12,11 +16,20 @@
Platform.SWITCH,
]

CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Alexa Devices component."""
async_setup_services(hass)
return True


async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Set up Alexa Devices platform."""

coordinator = AmazonDevicesCoordinator(hass, entry)
session = aiohttp_client.async_create_clientsession(hass)
coordinator = AmazonDevicesCoordinator(hass, entry, session)

await coordinator.async_config_entry_first_refresh()

Expand All @@ -29,8 +42,4 @@ async def async_setup_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bo

async def async_unload_entry(hass: HomeAssistant, entry: AmazonConfigEntry) -> bool:
"""Unload a config entry."""
coordinator = entry.runtime_data
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
await coordinator.api.close()

return unload_ok
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
10 changes: 4 additions & 6 deletions homeassistant/components/alexa_devices/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_CODE, CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.selector import CountrySelector

Expand All @@ -33,18 +34,15 @@
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> dict[str, Any]:
"""Validate the user input allows us to connect."""

session = aiohttp_client.async_create_clientsession(hass)
api = AmazonEchoApi(
session,
data[CONF_COUNTRY],
data[CONF_USERNAME],
data[CONF_PASSWORD],
)

try:
data = await api.login_mode_interactive(data[CONF_CODE])
finally:
await api.close()

return data
return await api.login_mode_interactive(data[CONF_CODE])


class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN):
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/alexa_devices/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
CannotConnect,
CannotRetrieveData,
)
from aiohttp import ClientSession

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_COUNTRY, CONF_PASSWORD, CONF_USERNAME
Expand All @@ -31,6 +32,7 @@ def __init__(
self,
hass: HomeAssistant,
entry: AmazonConfigEntry,
session: ClientSession,
) -> None:
"""Initialize the scanner."""
super().__init__(
Expand All @@ -41,6 +43,7 @@ def __init__(
update_interval=timedelta(seconds=SCAN_INTERVAL),
)
self.api = AmazonEchoApi(
session,
entry.data[CONF_COUNTRY],
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
Expand Down
8 changes: 8 additions & 0 deletions homeassistant/components/alexa_devices/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,13 @@
}
}
}
},
"services": {
"send_sound": {
"service": "mdi:cast-audio"
},
"send_text_command": {
"service": "mdi:microphone-message"
}
}
}
2 changes: 1 addition & 1 deletion homeassistant/components/alexa_devices/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "silver",
"requirements": ["aioamazondevices==3.5.1"]
"requirements": ["aioamazondevices==4.0.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/alexa_devices/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,5 @@ rules:

# Platinum
async-dependency: done
inject-websession: todo
inject-websession: done
strict-typing: done
121 changes: 121 additions & 0 deletions homeassistant/components/alexa_devices/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""Support for services."""

from aioamazondevices.sounds import SOUNDS_LIST
import voluptuous as vol

from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_DEVICE_ID
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv, device_registry as dr

from .const import DOMAIN
from .coordinator import AmazonConfigEntry

ATTR_TEXT_COMMAND = "text_command"
ATTR_SOUND = "sound"
ATTR_SOUND_VARIANT = "sound_variant"
SERVICE_TEXT_COMMAND = "send_text_command"
SERVICE_SOUND_NOTIFICATION = "send_sound"

SCHEMA_SOUND_SERVICE = vol.Schema(
{
vol.Required(ATTR_SOUND): cv.string,
vol.Required(ATTR_SOUND_VARIANT): cv.positive_int,
vol.Required(ATTR_DEVICE_ID): cv.string,
},
)
SCHEMA_CUSTOM_COMMAND = vol.Schema(
{
vol.Required(ATTR_TEXT_COMMAND): cv.string,
vol.Required(ATTR_DEVICE_ID): cv.string,
}
)


@callback
def async_get_entry_id_for_service_call(
call: ServiceCall,
) -> tuple[dr.DeviceEntry, AmazonConfigEntry]:
"""Get the entry ID related to a service call (by device ID)."""
device_registry = dr.async_get(call.hass)
device_id = call.data[ATTR_DEVICE_ID]
if (device_entry := device_registry.async_get(device_id)) is None:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_device_id",
translation_placeholders={"device_id": device_id},
)

for entry_id in device_entry.config_entries:
if (entry := call.hass.config_entries.async_get_entry(entry_id)) is None:
continue
if entry.domain == DOMAIN:
if entry.state is not ConfigEntryState.LOADED:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="entry_not_loaded",
translation_placeholders={"entry": entry.title},
)
return (device_entry, entry)

raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="config_entry_not_found",
translation_placeholders={"device_id": device_id},
)


async def _async_execute_action(call: ServiceCall, attribute: str) -> None:
"""Execute action on the device."""
device, config_entry = async_get_entry_id_for_service_call(call)
assert device.serial_number
value: str = call.data[attribute]

coordinator = config_entry.runtime_data

if attribute == ATTR_SOUND:
variant: int = call.data[ATTR_SOUND_VARIANT]
pad = "_" if variant > 10 else "_0"
file = f"{value}{pad}{variant!s}"
if value not in SOUNDS_LIST or variant > SOUNDS_LIST[value]:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_sound_value",
translation_placeholders={"sound": value, "variant": str(variant)},
)
await coordinator.api.call_alexa_sound(
coordinator.data[device.serial_number], file
)
elif attribute == ATTR_TEXT_COMMAND:
await coordinator.api.call_alexa_text_command(
coordinator.data[device.serial_number], value
)


async def async_send_sound_notification(call: ServiceCall) -> None:
"""Send a sound notification to a AmazonDevice."""
await _async_execute_action(call, ATTR_SOUND)


async def async_send_text_command(call: ServiceCall) -> None:
"""Send a custom command to a AmazonDevice."""
await _async_execute_action(call, ATTR_TEXT_COMMAND)


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up the services for the Amazon Devices integration."""
for service_name, method, schema in (
(
SERVICE_SOUND_NOTIFICATION,
async_send_sound_notification,
SCHEMA_SOUND_SERVICE,
),
(
SERVICE_TEXT_COMMAND,
async_send_text_command,
SCHEMA_CUSTOM_COMMAND,
),
):
hass.services.async_register(DOMAIN, service_name, method, schema=schema)
Loading
Loading