Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
00dfc04
Skip processing request body for HTTP HEAD requests (#147899)
AudunVN Jul 2, 2025
9c49512
Bump deebot-client to 13.5.0 (#147938)
edenhaus Jul 2, 2025
ec65066
Update mypy-dev to 1.17.0a4 (#147939)
cdce8p Jul 2, 2025
73e505d
Update pytest-xdist to 3.8.0 (#147943)
cdce8p Jul 2, 2025
6c7da57
Update pytest-cov to 6.2.1 (#147942)
cdce8p Jul 2, 2025
1051f85
Update coverage to 7.9.1 (#147940)
cdce8p Jul 2, 2025
bab9ec9
Add sensor for online status to PlayStation Network (#147842)
tr4nt0r Jul 2, 2025
7ff90ca
Open repair issue when outbound WebSocket is enabled for Shelly non-s…
bieniu Jul 2, 2025
73251fb
Handle additional errors in Nord Pool (#147937)
gjohansson-ST Jul 2, 2025
cb8e076
Fix missing device_class and state_class on compensation entities (#1…
Petro31 Jul 2, 2025
f77e6cc
Add missing exception translations to LCN (#147723)
alengwenus Jul 2, 2025
bbe03dc
Add missing Opower tests (#147934)
tronikos Jul 2, 2025
a7002e3
Update pytest to 8.4.1 (#147951)
cdce8p Jul 2, 2025
f10fcde
Remove the deprecated interface paramater for velbus (#147868)
cereal2nd Jul 2, 2025
ff76017
Simplify unnecessary re match.groups()[0] calls (#147909)
scop Jul 2, 2025
57a9824
Update frontend to 20250702.0 (#147952)
bramkragten Jul 2, 2025
b7496be
Bump aioamazondevices to 3.2.2 (#147953)
chemelli74 Jul 2, 2025
3d27c0c
SMA add DHCP strictness (#145753)
erwindouna Jul 2, 2025
7447cf3
UnifiProtect Change log level from debug to error for connection exce…
RaHehl Jul 2, 2025
943fb99
Adjust logic related to entity platform state (#147882)
emontnemery Jul 2, 2025
f50ef79
Ollama: Migrate pick model to subentry (#147944)
balloob Jul 2, 2025
d6da686
Z-Wave JS: rename controller to adapter according to term decision (#…
c0ffeeca7 Jul 2, 2025
adec157
Allow trigger based numeric sensors to be set to unknown (#137047)
Petro31 Jul 2, 2025
3778f53
Remove noisy debug logs in Husgvarna Automower (#147958)
Thomas55555 Jul 2, 2025
80a1e0e
Improve huawei_lte config flow class naming (#147910)
scop Jul 2, 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: 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": "bronze",
"requirements": ["aioamazondevices==3.2.1"]
"requirements": ["aioamazondevices==3.2.2"]
}
24 changes: 17 additions & 7 deletions homeassistant/components/compensation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@
import numpy as np
import voluptuous as vol

from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.sensor import (
CONF_STATE_CLASS,
DEVICE_CLASSES_SCHEMA as SENSOR_DEVICE_CLASSES_SCHEMA,
DOMAIN as SENSOR_DOMAIN,
STATE_CLASSES_SCHEMA as SENSOR_STATE_CLASSES_SCHEMA,
)
from homeassistant.const import (
CONF_ATTRIBUTE,
CONF_DEVICE_CLASS,
CONF_MAXIMUM,
CONF_MINIMUM,
CONF_NAME,
CONF_SOURCE,
CONF_UNIQUE_ID,
CONF_UNIT_OF_MEASUREMENT,
Expand Down Expand Up @@ -50,20 +57,23 @@ def datapoints_greater_than_degree(value: dict) -> dict:

COMPENSATION_SCHEMA = vol.Schema(
{
vol.Required(CONF_SOURCE): cv.entity_id,
vol.Optional(CONF_ATTRIBUTE): cv.string,
vol.Required(CONF_DATAPOINTS): [
vol.ExactSequence([vol.Coerce(float), vol.Coerce(float)])
],
vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(CONF_ATTRIBUTE): cv.string,
vol.Optional(CONF_UPPER_LIMIT, default=False): cv.boolean,
vol.Optional(CONF_LOWER_LIMIT, default=False): cv.boolean,
vol.Optional(CONF_PRECISION, default=DEFAULT_PRECISION): cv.positive_int,
vol.Optional(CONF_DEGREE, default=DEFAULT_DEGREE): vol.All(
vol.Coerce(int),
vol.Range(min=1, max=7),
),
vol.Optional(CONF_DEVICE_CLASS): SENSOR_DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_LOWER_LIMIT, default=False): cv.boolean,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_PRECISION, default=DEFAULT_PRECISION): cv.positive_int,
vol.Required(CONF_SOURCE): cv.entity_id,
vol.Optional(CONF_STATE_CLASS): SENSOR_STATE_CLASSES_SCHEMA,
vol.Optional(CONF_UNIQUE_ID): cv.string,
vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string,
vol.Optional(CONF_UPPER_LIMIT, default=False): cv.boolean,
}
)

Expand Down
82 changes: 55 additions & 27 deletions homeassistant/components/compensation/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@

import numpy as np

from homeassistant.components.sensor import SensorEntity
from homeassistant.components.sensor import (
ATTR_STATE_CLASS,
CONF_STATE_CLASS,
SensorEntity,
)
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_UNIT_OF_MEASUREMENT,
CONF_ATTRIBUTE,
CONF_DEVICE_CLASS,
CONF_MAXIMUM,
CONF_MINIMUM,
CONF_NAME,
CONF_SOURCE,
CONF_UNIQUE_ID,
CONF_UNIT_OF_MEASUREMENT,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
)
from homeassistant.core import (
Expand Down Expand Up @@ -59,24 +67,13 @@ async def async_setup_platform(

source: str = conf[CONF_SOURCE]
attribute: str | None = conf.get(CONF_ATTRIBUTE)
name = f"{DEFAULT_NAME} {source}"
if attribute is not None:
name = f"{name} {attribute}"
if not (name := conf.get(CONF_NAME)):
name = f"{DEFAULT_NAME} {source}"
if attribute is not None:
name = f"{name} {attribute}"

async_add_entities(
[
CompensationSensor(
conf.get(CONF_UNIQUE_ID),
name,
source,
attribute,
conf[CONF_PRECISION],
conf[CONF_POLYNOMIAL],
conf.get(CONF_UNIT_OF_MEASUREMENT),
conf[CONF_MINIMUM],
conf[CONF_MAXIMUM],
)
]
[CompensationSensor(conf.get(CONF_UNIQUE_ID), name, source, attribute, conf)]
)


Expand All @@ -91,23 +88,27 @@ def __init__(
name: str,
source: str,
attribute: str | None,
precision: int,
polynomial: np.poly1d,
unit_of_measurement: str | None,
minimum: tuple[float, float] | None,
maximum: tuple[float, float] | None,
config: dict[str, Any],
) -> None:
"""Initialize the Compensation sensor."""

self._attr_name = name
self._source_entity_id = source
self._precision = precision
self._source_attribute = attribute
self._attr_native_unit_of_measurement = unit_of_measurement

self._precision = config[CONF_PRECISION]
self._attr_native_unit_of_measurement = config.get(CONF_UNIT_OF_MEASUREMENT)

polynomial: np.poly1d = config[CONF_POLYNOMIAL]
self._poly = polynomial
self._coefficients = polynomial.coefficients.tolist()

self._attr_unique_id = unique_id
self._attr_name = name
self._minimum = minimum
self._maximum = maximum
self._minimum = config[CONF_MINIMUM]
self._maximum = config[CONF_MAXIMUM]

self._attr_device_class = config.get(CONF_DEVICE_CLASS)
self._attr_state_class = config.get(CONF_STATE_CLASS)

async def async_added_to_hass(self) -> None:
"""Handle added to Hass."""
Expand Down Expand Up @@ -137,13 +138,40 @@ def _async_compensation_sensor_state_listener(
"""Handle sensor state changes."""
new_state: State | None
if (new_state := event.data["new_state"]) is None:
_LOGGER.warning(
"While updating compensation %s, the new_state is None", self.name
)
self._attr_native_value = None
self.async_write_ha_state()
return

if new_state.state == STATE_UNKNOWN:
self._attr_native_value = None
self.async_write_ha_state()
return

if new_state.state == STATE_UNAVAILABLE:
self._attr_available = False
self.async_write_ha_state()
return

self._attr_available = True

if self.native_unit_of_measurement is None and self._source_attribute is None:
self._attr_native_unit_of_measurement = new_state.attributes.get(
ATTR_UNIT_OF_MEASUREMENT
)

if self._attr_device_class is None and (
device_class := new_state.attributes.get(ATTR_DEVICE_CLASS)
):
self._attr_device_class = device_class

if self._attr_state_class is None and (
state_class := new_state.attributes.get(ATTR_STATE_CLASS)
):
self._attr_state_class = state_class

if self._source_attribute:
value = new_state.attributes.get(self._source_attribute)
else:
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/ecovacs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/ecovacs",
"iot_class": "cloud_push",
"loggers": ["sleekxmppfs", "sucks", "deebot_client"],
"requirements": ["py-sucks==0.9.11", "deebot-client==13.4.0"]
"requirements": ["py-sucks==0.9.11", "deebot-client==13.5.0"]
}
2 changes: 1 addition & 1 deletion homeassistant/components/frontend/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@
"documentation": "https://www.home-assistant.io/integrations/frontend",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["home-assistant-frontend==20250701.0"]
"requirements": ["home-assistant-frontend==20250702.0"]
}
8 changes: 6 additions & 2 deletions homeassistant/components/hassio/ingress.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import aiohttp
from aiohttp import ClientTimeout, ClientWebSocketResponse, hdrs, web
from aiohttp.helpers import must_be_empty_body
from aiohttp.web_exceptions import HTTPBadGateway, HTTPBadRequest
from multidict import CIMultiDict
from yarl import URL
Expand Down Expand Up @@ -184,13 +185,16 @@ async def _handle_request(
content_type = "application/octet-stream"

# Simple request
if result.status in (204, 304) or (
if (empty_body := must_be_empty_body(result.method, result.status)) or (
content_length is not UNDEFINED
and (content_length_int := int(content_length))
<= MAX_SIMPLE_RESPONSE_SIZE
):
# Return Response
body = await result.read()
if empty_body:
body = None
else:
body = await result.read()
simple_response = web.Response(
headers=headers,
status=result.status,
Expand Down
10 changes: 5 additions & 5 deletions homeassistant/components/huawei_lte/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@
_LOGGER = logging.getLogger(__name__)


class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
"""Handle Huawei LTE config flow."""
class HuaweiLteConfigFlow(ConfigFlow, domain=DOMAIN):
"""Huawei LTE config flow."""

VERSION = 3

Expand All @@ -75,9 +75,9 @@ class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> OptionsFlowHandler:
) -> HuaweiLteOptionsFlow:
"""Get options flow."""
return OptionsFlowHandler()
return HuaweiLteOptionsFlow()

async def _async_show_user_form(
self,
Expand Down Expand Up @@ -354,7 +354,7 @@ async def async_step_reauth_confirm(
return self.async_update_reload_and_abort(entry, data=new_data)


class OptionsFlowHandler(OptionsFlow):
class HuaweiLteOptionsFlow(OptionsFlow):
"""Huawei LTE options flow."""

async def async_step_init(
Expand Down
1 change: 0 additions & 1 deletion homeassistant/components/husqvarna_automower/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ def event(self) -> CalendarEvent | None:
schedule = self.mower_attributes.calendar
cursor = schedule.timeline.active_after(dt_util.now())
program_event = next(cursor, None)
_LOGGER.debug("program_event %s", program_event)
if not program_event:
return None
work_area_name = None
Expand Down
6 changes: 5 additions & 1 deletion homeassistant/components/lcn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: LcnConfigEntry) -
) as ex:
await lcn_connection.async_close()
raise ConfigEntryNotReady(
f"Unable to connect to {config_entry.title}: {ex}"
translation_domain=DOMAIN,
translation_key="cannot_connect",
translation_placeholders={
"config_entry_title": config_entry.title,
},
) from ex

_LOGGER.info('LCN connected to "%s"', config_entry.title)
Expand Down
11 changes: 9 additions & 2 deletions homeassistant/components/lcn/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
CONF_SWITCHES,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.typing import ConfigType

Expand Down Expand Up @@ -100,7 +101,11 @@ def get_resource(domain_name: str, domain_data: ConfigType) -> str:
return cast(str, domain_data["setpoint"])
if domain_name == "scene":
return f"{domain_data['register']}{domain_data['scene']}"
raise ValueError("Unknown domain")
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="invalid_domain",
translation_placeholders={CONF_DOMAIN: domain_name},
)


def generate_unique_id(
Expand Down Expand Up @@ -304,6 +309,8 @@ def get_device_config(
def is_states_string(states_string: str) -> list[str]:
"""Validate the given states string and return states list."""
if len(states_string) != 8:
raise ValueError("Invalid length of states string")
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="invalid_length_of_states_string"
)
states = {"1": "ON", "0": "OFF", "T": "TOGGLE", "-": "NOCHANGE"}
return [states[state_string] for state_string in states_string]
2 changes: 1 addition & 1 deletion homeassistant/components/lcn/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ rules:
test-before-setup: done
unique-config-entry: done
# Silver
action-exceptions: todo
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters:
status: exempt
Expand Down
10 changes: 6 additions & 4 deletions homeassistant/components/lcn/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,9 @@ async def async_call_service(self, service: ServiceCall) -> None:
if (delay_time := service.data[CONF_TIME]) != 0:
hit = pypck.lcn_defs.SendKeyCommand.HIT
if pypck.lcn_defs.SendKeyCommand[service.data[CONF_STATE]] != hit:
raise ValueError(
"Only hit command is allowed when sending deferred keys."
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_send_keys_action",
)
delay_unit = pypck.lcn_defs.TimeUnit.parse(service.data[CONF_TIME_UNIT])
await device_connection.send_keys_hit_deferred(keys, delay_time, delay_unit)
Expand Down Expand Up @@ -368,8 +369,9 @@ async def async_call_service(self, service: ServiceCall) -> None:

if (delay_time := service.data[CONF_TIME]) != 0:
if table_id != 0:
raise ValueError(
"Only table A is allowed when locking keys for a specific time."
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_lock_keys_table",
)
delay_unit = pypck.lcn_defs.TimeUnit.parse(service.data[CONF_TIME_UNIT])
await device_connection.lock_keys_tab_a_temporary(
Expand Down
18 changes: 15 additions & 3 deletions homeassistant/components/lcn/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -414,11 +414,23 @@
}
},
"exceptions": {
"invalid_address": {
"message": "LCN device for given address has not been configured."
"cannot_connect": {
"message": "Unable to connect to {config_entry_title}."
},
"invalid_device_id": {
"message": "LCN device for given device ID has not been configured."
"message": "LCN device for given device ID {device_id} has not been configured."
},
"invalid_domain": {
"message": "Invalid domain {domain}."
},
"invalid_send_keys_action": {
"message": "Invalid state for sending keys. Only 'hit' allowed for deferred sending."
},
"invalid_lock_keys_table": {
"message": "Invalid table for locking keys. Only table A allowed when locking for a specific time."
},
"invalid_length_of_states_string": {
"message": "Invalid length of states string. Expected 8 characters."
}
}
}
3 changes: 3 additions & 0 deletions homeassistant/components/nordpool/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datetime import datetime, timedelta
from typing import TYPE_CHECKING

import aiohttp
from pynordpool import (
Currency,
DeliveryPeriodData,
Expand Down Expand Up @@ -91,6 +92,8 @@ async def api_call(self, retry: int = 3) -> DeliveryPeriodsData | None:
except (
NordPoolResponseError,
NordPoolError,
TimeoutError,
aiohttp.ClientError,
) as error:
LOGGER.debug("Connection error: %s", error)
self.async_set_update_error(error)
Expand Down
Loading
Loading