Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b1ae9c9
Add a switch entity for add-ons (#151431)
felipecrs Sep 23, 2025
3f70084
Handle ignored and disabled entries correctly in zeroconf discovery f…
rohankapoorcom Sep 23, 2025
29a42a8
Add analytics platform to automation (#152828)
arturpragacz Sep 23, 2025
014881d
Fix error handling in subscription info retrieval and update tests (#…
ludeeus Sep 23, 2025
f00ab80
Add analytics platform to template (#152824)
arturpragacz Sep 23, 2025
a78c909
Rename cover property in tuya (#152822)
epenet Sep 23, 2025
5d543d2
Bump pydroplet version to 2.3.3 (#152832)
sarahseidman Sep 23, 2025
a2a726d
Rename function arguments in modbus (#152814)
epenet Sep 23, 2025
2ab051b
Bump yt-dlp to 2025.09.23 (#152818)
andreimoraru Sep 23, 2025
ca18692
Add Matter Thermostat OutdoorTemperature sensor (#152632)
lboue Sep 23, 2025
874ca13
Simplified ZHA adapter migration and setup flow (#152389)
puddly Sep 23, 2025
15cc28e
Move first probe firmware to firmware progress in hardware flow (#152…
MartinHjelmare Sep 23, 2025
20293e2
Bump aiohasupervisor to 0.3.3b0 (#152835)
agners Sep 23, 2025
3bac6b8
Fix multiple_here_travel_time_entries issue description (#152839)
eifinger Sep 23, 2025
3bc2ea7
Use DOMAIN not MODBUS_DOMAIN (#152823)
janiversen Sep 23, 2025
60bf298
File add read_file action with Response (#139216)
PeteRager Sep 23, 2025
2008a73
Add support for Hue MotionAware sensors (#152811)
marcelveldt Sep 23, 2025
911f901
Bump aioesphomeapi to 41.9.0 (#152841)
bdraco Sep 23, 2025
9ba7dda
Rename logbook integration to "Activity" in user-facing strings (#150…
Copilot Sep 23, 2025
ff47839
Fix support for new Hue bulbs with very wide color temperature suppor…
marcelveldt Sep 23, 2025
a0be737
Auto select first active wake word (#152562)
synesthesiam Sep 23, 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
2 changes: 2 additions & 0 deletions homeassistant/components/analytics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
AnalyticsModifications,
DeviceAnalyticsModifications,
EntityAnalyticsModifications,
async_devices_payload,
)
from .const import ATTR_ONBOARDED, ATTR_PREFERENCES, DOMAIN, INTERVAL, PREFERENCE_SCHEMA
from .http import AnalyticsDevicesView
Expand All @@ -27,6 +28,7 @@
"AnalyticsModifications",
"DeviceAnalyticsModifications",
"EntityAnalyticsModifications",
"async_devices_payload",
]

CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
Expand Down
24 changes: 24 additions & 0 deletions homeassistant/components/automation/analytics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Analytics platform."""

from homeassistant.components.analytics import (
AnalyticsInput,
AnalyticsModifications,
EntityAnalyticsModifications,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er


async def async_modify_analytics(
hass: HomeAssistant, analytics_input: AnalyticsInput
) -> AnalyticsModifications:
"""Modify the analytics."""
ent_reg = er.async_get(hass)

entities: dict[str, EntityAnalyticsModifications] = {}
for entity_id in analytics_input.entity_ids:
entity_entry = ent_reg.entities[entity_id]
if entity_entry.capabilities is not None:
entities[entity_id] = EntityAnalyticsModifications(capabilities=None)

return AnalyticsModifications(entities=entities)
6 changes: 5 additions & 1 deletion homeassistant/components/cloud/subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ async def async_subscription_info(cloud: Cloud[CloudClient]) -> SubscriptionInfo
return await cloud.payments.subscription_info()
except PaymentsApiError as exception:
_LOGGER.error("Failed to fetch subscription information - %s", exception)

except TimeoutError:
_LOGGER.error(
"A timeout of %s was reached while trying to fetch subscription information",
REQUEST_TIMEOUT,
)
return None


Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/default_config/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"conversation",
"dhcp",
"energy",
"file",
"go2rtc",
"history",
"homeassistant_alerts",
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/droplet/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/droplet",
"iot_class": "local_push",
"quality_scale": "bronze",
"requirements": ["pydroplet==2.3.2"],
"requirements": ["pydroplet==2.3.3"],
"zeroconf": ["_droplet._tcp.local."]
}
2 changes: 1 addition & 1 deletion homeassistant/components/esphome/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"mqtt": ["esphome/discover/#"],
"quality_scale": "platinum",
"requirements": [
"aioesphomeapi==41.6.0",
"aioesphomeapi==41.9.0",
"esphome-dashboard-api==1.3.0",
"bleak-esphome==3.3.0"
],
Expand Down
15 changes: 15 additions & 0 deletions homeassistant/components/esphome/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,21 @@ def async_satellite_config_updated(
self._attr_options = [NO_WAKE_WORD, *sorted(self._wake_words)]

option = self._attr_current_option

if (
(self._wake_word_index == 0)
and (len(config.active_wake_words) == 1)
and (option in (None, NO_WAKE_WORD))
):
option = next(
(
wake_word
for wake_word, wake_word_id in self._wake_words.items()
if wake_word_id == config.active_wake_words[0]
),
None,
)

if (
(option is None)
or ((wake_word_id := self._wake_words.get(option)) is None)
Expand Down
11 changes: 11 additions & 0 deletions homeassistant/components/file/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,22 @@
from homeassistant.const import CONF_FILE_PATH, CONF_NAME, CONF_PLATFORM, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType

from .const import DOMAIN
from .services import async_register_services

PLATFORMS = [Platform.NOTIFY, Platform.SENSOR]

CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)


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


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a file component entry."""
Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/file/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@

DEFAULT_NAME = "File"
FILE_ICON = "mdi:file"

SERVICE_READ_FILE = "read_file"
ATTR_FILE_NAME = "file_name"
ATTR_FILE_ENCODING = "file_encoding"
7 changes: 7 additions & 0 deletions homeassistant/components/file/icons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"services": {
"read_file": {
"service": "mdi:file"
}
}
}
88 changes: 88 additions & 0 deletions homeassistant/components/file/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""File Service calls."""

from collections.abc import Callable
import json

import voluptuous as vol
import yaml

from homeassistant.core import HomeAssistant, ServiceCall, SupportsResponse
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv

from .const import ATTR_FILE_ENCODING, ATTR_FILE_NAME, DOMAIN, SERVICE_READ_FILE


def async_register_services(hass: HomeAssistant) -> None:
"""Register services for File integration."""

if not hass.services.has_service(DOMAIN, SERVICE_READ_FILE):
hass.services.async_register(
DOMAIN,
SERVICE_READ_FILE,
read_file,
schema=vol.Schema(
{
vol.Required(ATTR_FILE_NAME): cv.string,
vol.Required(ATTR_FILE_ENCODING): cv.string,
}
),
supports_response=SupportsResponse.ONLY,
)


ENCODING_LOADERS: dict[str, tuple[Callable, type[Exception]]] = {
"json": (json.loads, json.JSONDecodeError),
"yaml": (yaml.safe_load, yaml.YAMLError),
}


def read_file(call: ServiceCall) -> dict:
"""Handle read_file service call."""
file_name = call.data[ATTR_FILE_NAME]
file_encoding = call.data[ATTR_FILE_ENCODING].lower()

if not call.hass.config.is_allowed_path(file_name):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="no_access_to_path",
translation_placeholders={"filename": file_name},
)

if file_encoding not in ENCODING_LOADERS:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="unsupported_file_encoding",
translation_placeholders={
"filename": file_name,
"encoding": file_encoding,
},
)

try:
with open(file_name, encoding="utf-8") as file:
file_content = file.read()
except FileNotFoundError as err:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="file_not_found",
translation_placeholders={"filename": file_name},
) from err
except OSError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="file_read_error",
translation_placeholders={"filename": file_name},
) from err

loader, error_type = ENCODING_LOADERS[file_encoding]
try:
data = loader(file_content)
except error_type as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="file_decoding",
translation_placeholders={"filename": file_name, "encoding": file_encoding},
) from err

return {"data": data}
14 changes: 14 additions & 0 deletions homeassistant/components/file/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Describes the format for available file services
read_file:
fields:
file_name:
example: "www/my_file.json"
selector:
text:
file_encoding:
example: "JSON"
selector:
select:
options:
- "JSON"
- "YAML"
31 changes: 31 additions & 0 deletions homeassistant/components/file/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,37 @@
},
"write_access_failed": {
"message": "Write access to {filename} failed: {exc}."
},
"no_access_to_path": {
"message": "Cannot read {filename}, no access to path; `allowlist_external_dirs` may need to be adjusted in `configuration.yaml`"
},
"unsupported_file_encoding": {
"message": "Cannot read {filename}, unsupported file encoding {encoding}."
},
"file_decoding": {
"message": "Cannot read file {filename} as {encoding}."
},
"file_not_found": {
"message": "File {filename} not found."
},
"file_read_error": {
"message": "Error reading {filename}."
}
},
"services": {
"read_file": {
"name": "Read file",
"description": "Reads a file and returns the contents.",
"fields": {
"file_name": {
"name": "File name",
"description": "Name of the file to read."
},
"file_encoding": {
"name": "File encoding",
"description": "Encoding of the file (JSON, YAML.)"
}
}
}
}
}
3 changes: 2 additions & 1 deletion homeassistant/components/hassio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
config_flow,
diagnostics,
sensor,
switch,
system_health,
update,
)
Expand Down Expand Up @@ -149,7 +150,7 @@
# If new platforms are added, be sure to import them above
# so we do not make other components that depend on hassio
# wait for the import of the platforms
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.UPDATE]
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SWITCH, Platform.UPDATE]

CONF_FRONTEND_REPO = "development_repo"

Expand Down
13 changes: 13 additions & 0 deletions homeassistant/components/hassio/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import asyncio
from collections import defaultdict
from copy import deepcopy
import logging
from typing import TYPE_CHECKING, Any

Expand Down Expand Up @@ -545,3 +546,15 @@ async def _async_refresh(
await super()._async_refresh(
log_failures, raise_on_auth_failed, scheduled, raise_on_entry_error
)

async def force_addon_info_data_refresh(self, addon_slug: str) -> None:
"""Force refresh of addon info data for a specific addon."""
try:
slug, info = await self._update_addon_info(addon_slug)
if info is not None and DATA_KEY_ADDONS in self.data:
if slug in self.data[DATA_KEY_ADDONS]:
data = deepcopy(self.data)
data[DATA_KEY_ADDONS][slug].update(info)
self.async_set_updated_data(data)
except SupervisorError as err:
_LOGGER.warning("Could not refresh info for %s: %s", addon_slug, err)
2 changes: 1 addition & 1 deletion homeassistant/components/hassio/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/hassio",
"iot_class": "local_polling",
"quality_scale": "internal",
"requirements": ["aiohasupervisor==0.3.2"],
"requirements": ["aiohasupervisor==0.3.3b0"],
"single_config_entry": true
}
4 changes: 4 additions & 0 deletions homeassistant/components/hassio/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@
"unsupported_os_version": {
"title": "Unsupported system - Home Assistant OS version",
"description": "System is unsupported because the Home Assistant OS version in use is not supported. For troubleshooting information, select Learn more."
},
"unsupported_home_assistant_core_version": {
"title": "Unsupported system - Home Assistant Core version",
"description": "System is unsupported because the Home Assistant Core version in use is not supported. For troubleshooting information, select Learn more."
}
},
"entity": {
Expand Down
Loading
Loading