mirror of
https://github.com/home-assistant/core.git
synced 2026-04-16 21:17:20 +00:00
Compare commits
15 Commits
improve_ai
...
edenhaus/h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97fb03a1fa | ||
|
|
1f6e078d1d | ||
|
|
71d857b5e1 | ||
|
|
0de75a013b | ||
|
|
f87ec0a7b8 | ||
|
|
2cc1e74b87 | ||
|
|
6d1bd15256 | ||
|
|
9fe9064884 | ||
|
|
f9f57b00bb | ||
|
|
2b65b06003 | ||
|
|
206c498027 | ||
|
|
0ac62b241e | ||
|
|
4ba123a1a8 | ||
|
|
8b8b39c1b7 | ||
|
|
5b70e5f829 |
5
Dockerfile
generated
5
Dockerfile
generated
@@ -28,8 +28,7 @@ COPY rootfs /
|
||||
COPY --from=ghcr.io/alexxit/go2rtc@sha256:675c318b23c06fd862a61d262240c9a63436b4050d177ffc68a32710d9e05bae /usr/local/bin/go2rtc /bin/go2rtc
|
||||
|
||||
## Setup Home Assistant Core dependencies
|
||||
COPY requirements.txt homeassistant/
|
||||
COPY homeassistant/package_constraints.txt homeassistant/homeassistant/
|
||||
COPY --parents requirements.txt homeassistant/package_constraints.txt homeassistant/
|
||||
RUN \
|
||||
# Verify go2rtc can be executed
|
||||
go2rtc --version \
|
||||
@@ -49,7 +48,7 @@ RUN \
|
||||
-r homeassistant/requirements_all.txt
|
||||
|
||||
## Setup Home Assistant Core
|
||||
COPY . homeassistant/
|
||||
COPY --parents LICENSE* README* homeassistant/ pyproject.toml homeassistant/
|
||||
RUN \
|
||||
uv pip install \
|
||||
-e ./homeassistant \
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/camera",
|
||||
"integration_type": "entity",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["PyTurboJPEG==1.8.3"]
|
||||
"requirements": ["PyTurboJPEG==2.2.0"]
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ SPH_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
|
||||
key="mix_export_to_grid",
|
||||
translation_key="mix_export_to_grid",
|
||||
api_key="pacToGridTotal",
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
@@ -80,7 +80,7 @@ SPH_SENSOR_TYPES: tuple[GrowattSensorEntityDescription, ...] = (
|
||||
key="mix_import_from_grid",
|
||||
translation_key="mix_import_from_grid",
|
||||
api_key="pacToUserR",
|
||||
native_unit_of_measurement=UnitOfPower.KILO_WATT,
|
||||
native_unit_of_measurement=UnitOfPower.WATT,
|
||||
device_class=SensorDeviceClass.POWER,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
),
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["imgw_pib==2.0.2"]
|
||||
"requirements": ["imgw_pib==2.1.0"]
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "hub",
|
||||
"iot_class": "cloud_push",
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["pyaxencoapi==1.0.6"]
|
||||
"requirements": ["pyaxencoapi==1.0.7"]
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ from collections.abc import Iterator
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from soco import SoCo
|
||||
from soco import SoCo, SoCoException
|
||||
from soco.alarms import Alarm, Alarms
|
||||
from soco.events_base import Event as SonosEvent
|
||||
|
||||
@@ -30,6 +30,7 @@ class SonosAlarms(SonosHouseholdCoordinator):
|
||||
super().__init__(*args)
|
||||
self.alarms: Alarms = Alarms()
|
||||
self.created_alarm_ids: set[str] = set()
|
||||
self._household_mismatch_logged = False
|
||||
|
||||
def __iter__(self) -> Iterator:
|
||||
"""Return an iterator for the known alarms."""
|
||||
@@ -76,21 +77,40 @@ class SonosAlarms(SonosHouseholdCoordinator):
|
||||
await self.async_update_entities(speaker.soco, event_id)
|
||||
|
||||
@soco_error()
|
||||
def update_cache(self, soco: SoCo, update_id: int | None = None) -> bool:
|
||||
"""Update cache of known alarms and return if cache has changed."""
|
||||
self.alarms.update(soco)
|
||||
def update_cache(
|
||||
self,
|
||||
soco: SoCo,
|
||||
update_id: int | None = None,
|
||||
) -> bool:
|
||||
"""Update cache of known alarms and return whether any were seen."""
|
||||
try:
|
||||
self.alarms.update(soco)
|
||||
except SoCoException as err:
|
||||
err_msg = str(err)
|
||||
# Only catch the specific household mismatch error
|
||||
if "Alarm list UID" in err_msg and "does not match" in err_msg:
|
||||
if not self._household_mismatch_logged:
|
||||
_LOGGER.warning(
|
||||
"Sonos alarms for %s cannot be updated due to a household mismatch. "
|
||||
"This is a known limitation in setups with multiple households. "
|
||||
"You can safely ignore this warning, or to silence it, remove the "
|
||||
"affected household from your Sonos system. Error: %s",
|
||||
soco.player_name,
|
||||
err_msg,
|
||||
)
|
||||
self._household_mismatch_logged = True
|
||||
return False
|
||||
# Let all other exceptions bubble up to be handled by @soco_error()
|
||||
raise
|
||||
|
||||
if update_id and self.alarms.last_id < update_id:
|
||||
# Skip updates if latest query result is outdated or lagging
|
||||
return False
|
||||
|
||||
if (
|
||||
self.last_processed_event_id
|
||||
and self.alarms.last_id <= self.last_processed_event_id
|
||||
):
|
||||
# Skip updates already processed
|
||||
return False
|
||||
|
||||
_LOGGER.debug(
|
||||
"Updating processed event %s from %s (was %s)",
|
||||
self.alarms.last_id,
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "system",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["PyTurboJPEG==1.8.3", "av==16.0.1", "numpy==2.3.2"]
|
||||
"requirements": ["PyTurboJPEG==2.2.0", "av==16.0.1", "numpy==2.3.2"]
|
||||
}
|
||||
|
||||
@@ -150,11 +150,6 @@ class PreRecordMessageProtocol(RtpDatagramProtocol):
|
||||
if self.transport is None:
|
||||
return
|
||||
|
||||
if self._audio_bytes is None:
|
||||
# 16Khz, 16-bit mono audio message
|
||||
file_path = Path(__file__).parent / self.file_name
|
||||
self._audio_bytes = file_path.read_bytes()
|
||||
|
||||
if self._audio_task is None:
|
||||
self._audio_task = self.hass.async_create_background_task(
|
||||
self._play_message(),
|
||||
@@ -162,6 +157,11 @@ class PreRecordMessageProtocol(RtpDatagramProtocol):
|
||||
)
|
||||
|
||||
async def _play_message(self) -> None:
|
||||
if self._audio_bytes is None:
|
||||
_LOGGER.debug("Loading audio from file %s", self.file_name)
|
||||
self._audio_bytes = await self._load_audio()
|
||||
_LOGGER.debug("Read %s bytes", len(self._audio_bytes))
|
||||
|
||||
await self.hass.async_add_executor_job(
|
||||
partial(
|
||||
self.send_audio,
|
||||
@@ -175,3 +175,8 @@ class PreRecordMessageProtocol(RtpDatagramProtocol):
|
||||
|
||||
# Allow message to play again
|
||||
self._audio_task = None
|
||||
|
||||
async def _load_audio(self) -> bytes:
|
||||
# 16Khz, 16-bit mono audio message
|
||||
file_path = Path(__file__).parent / self.file_name
|
||||
return await self.hass.async_add_executor_job(file_path.read_bytes)
|
||||
|
||||
@@ -245,6 +245,14 @@ ENERGY_SENSORS = [
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
value_fn=lambda status: status.energy_in_defrost,
|
||||
),
|
||||
WeHeatSensorEntityDescription(
|
||||
translation_key="electricity_used_standby",
|
||||
key="electricity_used_standby",
|
||||
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
|
||||
device_class=SensorDeviceClass.ENERGY,
|
||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||
value_fn=lambda status: status.energy_in_standby,
|
||||
),
|
||||
WeHeatSensorEntityDescription(
|
||||
translation_key="energy_output_heating",
|
||||
key="energy_output_heating",
|
||||
|
||||
@@ -96,6 +96,9 @@
|
||||
"electricity_used_heating": {
|
||||
"name": "Electricity used heating"
|
||||
},
|
||||
"electricity_used_standby": {
|
||||
"name": "Electricity used standby"
|
||||
},
|
||||
"energy_output": {
|
||||
"name": "Total energy output"
|
||||
},
|
||||
|
||||
@@ -38,13 +38,13 @@ ha-ffmpeg==3.2.2
|
||||
habluetooth==6.0.0
|
||||
hass-nabucasa==2.2.0
|
||||
hassil==3.5.0
|
||||
home-assistant-bluetooth==1.13.1
|
||||
home-assistant-bluetooth==2.0.0
|
||||
home-assistant-frontend==20260325.7
|
||||
home-assistant-intents==2026.3.24
|
||||
httpx==0.28.1
|
||||
ifaddr==0.2.0
|
||||
Jinja2==3.1.6
|
||||
lru-dict==1.3.0
|
||||
lru-dict==1.4.1
|
||||
mutagen==1.47.0
|
||||
openai==2.21.0
|
||||
orjson==3.11.8
|
||||
@@ -59,7 +59,7 @@ PyNaCl==1.6.2
|
||||
pyOpenSSL==26.0.0
|
||||
pyspeex-noise==1.0.2
|
||||
python-slugify==8.0.4
|
||||
PyTurboJPEG==1.8.3
|
||||
PyTurboJPEG==2.2.0
|
||||
PyYAML==6.0.3
|
||||
requests==2.33.1
|
||||
securetar==2026.4.1
|
||||
@@ -134,7 +134,7 @@ backoff>=2.0
|
||||
Brotli>=1.2.0
|
||||
|
||||
# ensure pydantic version does not float since it might have breaking changes
|
||||
pydantic==2.13.0
|
||||
pydantic==2.13.1
|
||||
|
||||
# Required for Python 3.14.0 compatibility (#119223).
|
||||
mashumaro>=3.17.0
|
||||
|
||||
@@ -51,10 +51,10 @@ dependencies = [
|
||||
# When bumping httpx, please check the version pins of
|
||||
# httpcore, anyio, and h11 in gen_requirements_all
|
||||
"httpx==0.28.1",
|
||||
"home-assistant-bluetooth==1.13.1",
|
||||
"home-assistant-bluetooth==2.0.0",
|
||||
"ifaddr==0.2.0",
|
||||
"Jinja2==3.1.6",
|
||||
"lru-dict==1.3.0",
|
||||
"lru-dict==1.4.1",
|
||||
"PyJWT==2.12.1",
|
||||
# PyJWT has loose dependency. We want the latest one.
|
||||
"cryptography==46.0.7",
|
||||
|
||||
6
requirements.txt
generated
6
requirements.txt
generated
@@ -26,13 +26,13 @@ fnv-hash-fast==2.0.2
|
||||
ha-ffmpeg==3.2.2
|
||||
hass-nabucasa==2.2.0
|
||||
hassil==3.5.0
|
||||
home-assistant-bluetooth==1.13.1
|
||||
home-assistant-bluetooth==2.0.0
|
||||
home-assistant-intents==2026.3.24
|
||||
httpx==0.28.1
|
||||
ifaddr==0.2.0
|
||||
infrared-protocols==1.2.0
|
||||
Jinja2==3.1.6
|
||||
lru-dict==1.3.0
|
||||
lru-dict==1.4.1
|
||||
mutagen==1.47.0
|
||||
orjson==3.11.8
|
||||
packaging>=23.1
|
||||
@@ -44,7 +44,7 @@ pymicro-vad==1.0.1
|
||||
pyOpenSSL==26.0.0
|
||||
pyspeex-noise==1.0.2
|
||||
python-slugify==8.0.4
|
||||
PyTurboJPEG==1.8.3
|
||||
PyTurboJPEG==2.2.0
|
||||
PyYAML==6.0.3
|
||||
requests==2.33.1
|
||||
securetar==2026.4.1
|
||||
|
||||
6
requirements_all.txt
generated
6
requirements_all.txt
generated
@@ -96,7 +96,7 @@ PyTransportNSW==0.1.1
|
||||
|
||||
# homeassistant.components.camera
|
||||
# homeassistant.components.stream
|
||||
PyTurboJPEG==1.8.3
|
||||
PyTurboJPEG==2.2.0
|
||||
|
||||
# homeassistant.components.vicare
|
||||
PyViCare==2.59.0
|
||||
@@ -1313,7 +1313,7 @@ ihcsdk==2.8.5
|
||||
imeon_inverter_api==0.4.0
|
||||
|
||||
# homeassistant.components.imgw_pib
|
||||
imgw_pib==2.0.2
|
||||
imgw_pib==2.1.0
|
||||
|
||||
# homeassistant.components.incomfort
|
||||
incomfort-client==0.7.0
|
||||
@@ -1980,7 +1980,7 @@ pyatv==0.17.0
|
||||
pyaussiebb==0.1.5
|
||||
|
||||
# homeassistant.components.myneomitis
|
||||
pyaxencoapi==1.0.6
|
||||
pyaxencoapi==1.0.7
|
||||
|
||||
# homeassistant.components.balboa
|
||||
pybalboa==1.1.3
|
||||
|
||||
@@ -16,7 +16,7 @@ license-expression==30.4.3
|
||||
mock-open==1.4.0
|
||||
mypy==1.20.1
|
||||
prek==0.2.28
|
||||
pydantic==2.13.0
|
||||
pydantic==2.13.1
|
||||
pylint==4.0.5
|
||||
pylint-per-file-ignores==3.2.1
|
||||
pipdeptree==2.26.1
|
||||
|
||||
6
requirements_test_all.txt
generated
6
requirements_test_all.txt
generated
@@ -93,7 +93,7 @@ PyTransportNSW==0.1.1
|
||||
|
||||
# homeassistant.components.camera
|
||||
# homeassistant.components.stream
|
||||
PyTurboJPEG==1.8.3
|
||||
PyTurboJPEG==2.2.0
|
||||
|
||||
# homeassistant.components.vicare
|
||||
PyViCare==2.59.0
|
||||
@@ -1165,7 +1165,7 @@ igloohome-api==0.1.1
|
||||
imeon_inverter_api==0.4.0
|
||||
|
||||
# homeassistant.components.imgw_pib
|
||||
imgw_pib==2.0.2
|
||||
imgw_pib==2.1.0
|
||||
|
||||
# homeassistant.components.incomfort
|
||||
incomfort-client==0.7.0
|
||||
@@ -1717,7 +1717,7 @@ pyatv==0.17.0
|
||||
pyaussiebb==0.1.5
|
||||
|
||||
# homeassistant.components.myneomitis
|
||||
pyaxencoapi==1.0.6
|
||||
pyaxencoapi==1.0.7
|
||||
|
||||
# homeassistant.components.balboa
|
||||
pybalboa==1.1.3
|
||||
|
||||
@@ -124,7 +124,7 @@ backoff>=2.0
|
||||
Brotli>=1.2.0
|
||||
|
||||
# ensure pydantic version does not float since it might have breaking changes
|
||||
pydantic==2.13.0
|
||||
pydantic==2.13.1
|
||||
|
||||
# Required for Python 3.14.0 compatibility (#119223).
|
||||
mashumaro>=3.17.0
|
||||
|
||||
@@ -7,7 +7,6 @@ from homeassistant import core
|
||||
from homeassistant.util import executor, thread
|
||||
|
||||
from .model import Config, Integration
|
||||
from .requirements import PACKAGE_REGEX, PIP_VERSION_RANGE_SEPARATOR
|
||||
|
||||
_GO2RTC_SHA = (
|
||||
"675c318b23c06fd862a61d262240c9a63436b4050d177ffc68a32710d9e05bae" # 1.9.14
|
||||
@@ -43,8 +42,7 @@ COPY rootfs /
|
||||
COPY --from=ghcr.io/alexxit/go2rtc@sha256:{go2rtc} /usr/local/bin/go2rtc /bin/go2rtc
|
||||
|
||||
## Setup Home Assistant Core dependencies
|
||||
COPY requirements.txt homeassistant/
|
||||
COPY homeassistant/package_constraints.txt homeassistant/homeassistant/
|
||||
COPY --parents requirements.txt homeassistant/package_constraints.txt homeassistant/
|
||||
RUN \
|
||||
# Verify go2rtc can be executed
|
||||
go2rtc --version \
|
||||
@@ -64,7 +62,7 @@ RUN \
|
||||
-r homeassistant/requirements_all.txt
|
||||
|
||||
## Setup Home Assistant Core
|
||||
COPY . homeassistant/
|
||||
COPY --parents LICENSE* README* homeassistant/ pyproject.toml homeassistant/
|
||||
RUN \
|
||||
uv pip install \
|
||||
-e ./homeassistant \
|
||||
@@ -129,7 +127,7 @@ def _generate_machine_dockerfile(
|
||||
_HASSFEST_TEMPLATE = r"""# Automatically generated by hassfest.
|
||||
#
|
||||
# To update, run python3 -m script.hassfest -p docker
|
||||
FROM python:3.14-alpine
|
||||
FROM python:{python_version}-alpine
|
||||
|
||||
ENV \
|
||||
UV_SYSTEM_PYTHON=true \
|
||||
@@ -139,10 +137,12 @@ SHELL ["/bin/sh", "-o", "pipefail", "-c"]
|
||||
ENTRYPOINT ["/usr/src/homeassistant/script/hassfest/docker/entrypoint.sh"]
|
||||
WORKDIR "/github/workspace"
|
||||
|
||||
COPY . /usr/src/homeassistant
|
||||
COPY --parents requirements.txt homeassistant/ script /usr/src/homeassistant/
|
||||
|
||||
# Uv creates a lock file in /tmp
|
||||
RUN --mount=type=tmpfs,target=/tmp \
|
||||
--mount=type=bind,source=requirements_test.txt,target=/tmp/requirements_test.txt,readonly \
|
||||
--mount=type=bind,source=requirements_test_pre_commit.txt,target=/tmp/requirements_test_pre_commit.txt,readonly \
|
||||
# Required for PyTurboJPEG
|
||||
apk add --no-cache libturbojpeg \
|
||||
# Install uv at the version pinned in the requirements file
|
||||
@@ -152,9 +152,9 @@ RUN --mount=type=tmpfs,target=/tmp \
|
||||
--no-cache \
|
||||
-c /usr/src/homeassistant/homeassistant/package_constraints.txt \
|
||||
-r /usr/src/homeassistant/requirements.txt \
|
||||
pipdeptree=={pipdeptree} \
|
||||
tqdm=={tqdm} \
|
||||
ruff=={ruff}
|
||||
"pipdeptree==$(awk -F'==' '/^pipdeptree==/{{print $2}}' /tmp/requirements_test.txt)" \
|
||||
"tqdm==$(awk -F'==' '/^tqdm==/{{print $2}}' /tmp/requirements_test.txt)" \
|
||||
"ruff==$(awk -F'==' '/^ruff==/{{print $2}}' /tmp/requirements_test_pre_commit.txt)"
|
||||
|
||||
LABEL "name"="hassfest"
|
||||
LABEL "maintainer"="Home Assistant <hello@home-assistant.io>"
|
||||
@@ -166,34 +166,9 @@ LABEL "com.github.actions.color"="gray-dark"
|
||||
"""
|
||||
|
||||
|
||||
def _get_package_versions(file: Path, packages: set[str]) -> dict[str, str]:
|
||||
package_versions: dict[str, str] = {}
|
||||
with file.open(encoding="UTF-8") as fp:
|
||||
for _, line in enumerate(fp):
|
||||
if package_versions.keys() == packages:
|
||||
return package_versions
|
||||
|
||||
if match := PACKAGE_REGEX.match(line):
|
||||
pkg, sep, version = match.groups()
|
||||
|
||||
if pkg not in packages:
|
||||
continue
|
||||
|
||||
if sep != "==" or not version:
|
||||
raise RuntimeError(
|
||||
f'Requirement {pkg} need to be pinned "{pkg}==<version>".'
|
||||
)
|
||||
|
||||
for part in version.split(";", 1)[0].split(","):
|
||||
version_part = PIP_VERSION_RANGE_SEPARATOR.match(part)
|
||||
if version_part:
|
||||
package_versions[pkg] = version_part.group(2)
|
||||
break
|
||||
|
||||
if package_versions.keys() == packages:
|
||||
return package_versions
|
||||
|
||||
raise RuntimeError("At least one package was not found in the requirements file.")
|
||||
def _get_python_version(root: Path) -> str:
|
||||
"""Extract the Python version from .python-version."""
|
||||
return (root / ".python-version").read_text(encoding="UTF-8").strip()
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -215,26 +190,17 @@ def _generate_files(config: Config) -> list[File]:
|
||||
+ 10
|
||||
) * 1000
|
||||
|
||||
package_versions = _get_package_versions(
|
||||
config.root / "requirements_test.txt", {"pipdeptree", "tqdm"}
|
||||
)
|
||||
package_versions |= _get_package_versions(
|
||||
config.root / "requirements_test_pre_commit.txt", {"ruff"}
|
||||
)
|
||||
|
||||
files = [
|
||||
File(
|
||||
DOCKERFILE_TEMPLATE.format(
|
||||
timeout=timeout,
|
||||
**package_versions,
|
||||
go2rtc=_GO2RTC_SHA,
|
||||
),
|
||||
config.root / "Dockerfile",
|
||||
),
|
||||
File(
|
||||
_HASSFEST_TEMPLATE.format(
|
||||
timeout=timeout,
|
||||
**package_versions,
|
||||
python_version=_get_python_version(config.root),
|
||||
),
|
||||
config.root / "script/hassfest/docker/Dockerfile",
|
||||
),
|
||||
|
||||
12
script/hassfest/docker/Dockerfile
generated
12
script/hassfest/docker/Dockerfile
generated
@@ -1,7 +1,7 @@
|
||||
# Automatically generated by hassfest.
|
||||
#
|
||||
# To update, run python3 -m script.hassfest -p docker
|
||||
FROM python:3.14-alpine
|
||||
FROM python:3.14.2-alpine
|
||||
|
||||
ENV \
|
||||
UV_SYSTEM_PYTHON=true \
|
||||
@@ -11,10 +11,12 @@ SHELL ["/bin/sh", "-o", "pipefail", "-c"]
|
||||
ENTRYPOINT ["/usr/src/homeassistant/script/hassfest/docker/entrypoint.sh"]
|
||||
WORKDIR "/github/workspace"
|
||||
|
||||
COPY . /usr/src/homeassistant
|
||||
COPY --parents requirements.txt homeassistant/ script /usr/src/homeassistant/
|
||||
|
||||
# Uv creates a lock file in /tmp
|
||||
RUN --mount=type=tmpfs,target=/tmp \
|
||||
--mount=type=bind,source=requirements_test.txt,target=/tmp/requirements_test.txt,readonly \
|
||||
--mount=type=bind,source=requirements_test_pre_commit.txt,target=/tmp/requirements_test_pre_commit.txt,readonly \
|
||||
# Required for PyTurboJPEG
|
||||
apk add --no-cache libturbojpeg \
|
||||
# Install uv at the version pinned in the requirements file
|
||||
@@ -24,9 +26,9 @@ RUN --mount=type=tmpfs,target=/tmp \
|
||||
--no-cache \
|
||||
-c /usr/src/homeassistant/homeassistant/package_constraints.txt \
|
||||
-r /usr/src/homeassistant/requirements.txt \
|
||||
pipdeptree==2.26.1 \
|
||||
tqdm==4.67.1 \
|
||||
ruff==0.15.1
|
||||
"pipdeptree==$(awk -F'==' '/^pipdeptree==/{print $2}' /tmp/requirements_test.txt)" \
|
||||
"tqdm==$(awk -F'==' '/^tqdm==/{print $2}' /tmp/requirements_test.txt)" \
|
||||
"ruff==$(awk -F'==' '/^ruff==/{print $2}' /tmp/requirements_test_pre_commit.txt)"
|
||||
|
||||
LABEL "name"="hassfest"
|
||||
LABEL "maintainer"="Home Assistant <hello@home-assistant.io>"
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
# Ignore everything except the specified files
|
||||
*
|
||||
|
||||
!homeassistant/
|
||||
!requirements.txt
|
||||
!script/
|
||||
script/hassfest/docker/
|
||||
!script/hassfest/docker/entrypoint.sh
|
||||
# No need to include the Dockerfile
|
||||
script/hassfest/docker/Dockerfile*
|
||||
|
||||
# Temporary files
|
||||
**/__pycache__
|
||||
@@ -14143,7 +14143,7 @@
|
||||
'object_id_base': 'Export to grid',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 2,
|
||||
'suggested_display_precision': 0,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
@@ -14155,7 +14155,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'mix_export_to_grid',
|
||||
'unique_id': 'SPH123456-mix_export_to_grid',
|
||||
'unit_of_measurement': <UnitOfPower.KILO_WATT: 'kW'>,
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sph_sensors_v1_api[sensor.sph123456_export_to_grid-state]
|
||||
@@ -14164,7 +14164,7 @@
|
||||
'device_class': 'power',
|
||||
'friendly_name': 'SPH123456 Export to grid',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfPower.KILO_WATT: 'kW'>,
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sph123456_export_to_grid',
|
||||
@@ -14314,7 +14314,7 @@
|
||||
'object_id_base': 'Import from grid',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 2,
|
||||
'suggested_display_precision': 0,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.POWER: 'power'>,
|
||||
@@ -14326,7 +14326,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': 'mix_import_from_grid',
|
||||
'unique_id': 'SPH123456-mix_import_from_grid',
|
||||
'unit_of_measurement': <UnitOfPower.KILO_WATT: 'kW'>,
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_sph_sensors_v1_api[sensor.sph123456_import_from_grid-state]
|
||||
@@ -14335,7 +14335,7 @@
|
||||
'device_class': 'power',
|
||||
'friendly_name': 'SPH123456 Import from grid',
|
||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||
'unit_of_measurement': <UnitOfPower.KILO_WATT: 'kW'>,
|
||||
'unit_of_measurement': <UnitOfPower.WATT: 'W'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.sph123456_import_from_grid',
|
||||
|
||||
@@ -21,6 +21,8 @@ HYDROLOGICAL_DATA = HydrologicalData(
|
||||
water_temperature=SensorData(name="Water Temperature", value=10.8),
|
||||
flood_alarm=None,
|
||||
flood_warning=None,
|
||||
ice_phenomenon=SensorData(name="Ice Phenomenon", value=20),
|
||||
ice_phenomenon_measurement_date=datetime(2024, 4, 27, 10, 0, tzinfo=UTC),
|
||||
water_level_measurement_date=datetime(2024, 4, 27, 10, 0, tzinfo=UTC),
|
||||
water_temperature_measurement_date=datetime(2024, 4, 27, 10, 10, tzinfo=UTC),
|
||||
water_flow=SensorData(name="Water Flow", value=123.45),
|
||||
|
||||
@@ -41,6 +41,12 @@
|
||||
'valid_to': '2024-04-28T11:00:00+00:00',
|
||||
'value': 'rapid_water_level_rise',
|
||||
}),
|
||||
'ice_phenomenon': dict({
|
||||
'name': 'Ice Phenomenon',
|
||||
'unit': None,
|
||||
'value': 20,
|
||||
}),
|
||||
'ice_phenomenon_measurement_date': '2024-04-27T10:00:00+00:00',
|
||||
'latitude': None,
|
||||
'longitude': None,
|
||||
'river': 'River Name',
|
||||
|
||||
@@ -5,6 +5,7 @@ from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from soco.exceptions import SoCoException
|
||||
|
||||
from homeassistant.components.sonos import DOMAIN
|
||||
from homeassistant.components.sonos.const import (
|
||||
@@ -341,6 +342,28 @@ async def test_alarm_change_device(
|
||||
assert device.name == soco_br.get_speaker_info()["zone_name"]
|
||||
|
||||
|
||||
async def test_alarm_update_exception_logs_warning(
|
||||
hass: HomeAssistant,
|
||||
async_setup_sonos,
|
||||
entity_registry: er.EntityRegistry,
|
||||
soco: MockSoCo,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test household mismatch logs warning and alarm update/setup is skipped."""
|
||||
with patch(
|
||||
"homeassistant.components.sonos.alarms.Alarms.update",
|
||||
side_effect=SoCoException(
|
||||
"Alarm list UID RINCON_0001234567890:31 does not match RINCON_000E987654321:0"
|
||||
),
|
||||
):
|
||||
await async_setup_sonos()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Alarm should not be set up due to household mismatch
|
||||
assert "switch.sonos_alarm_14" not in entity_registry.entities
|
||||
assert "cannot be updated due to a household mismatch" in caplog.text
|
||||
|
||||
|
||||
async def test_alarm_setup_for_undiscovered_speaker(
|
||||
hass: HomeAssistant,
|
||||
async_setup_sonos,
|
||||
|
||||
@@ -11,6 +11,7 @@ from homeassistant.components.synology_dsm.const import (
|
||||
DOMAIN,
|
||||
SERVICES,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_MAC,
|
||||
@@ -22,7 +23,6 @@ from homeassistant.const import (
|
||||
CONF_VERIFY_SSL,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from .consts import HOST, MACS, PASSWORD, PORT, USE_SSL, USERNAME
|
||||
|
||||
@@ -57,19 +57,9 @@ async def test_services_registered(hass: HomeAssistant, mock_dsm: MagicMock) ->
|
||||
|
||||
async def test_reauth_triggered(hass: HomeAssistant) -> None:
|
||||
"""Test if reauthentication flow is triggered."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.synology_dsm.SynoApi.async_setup",
|
||||
side_effect=SynologyDSMLoginInvalidException(USERNAME),
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.synology_dsm.config_flow.SynologyDSMFlowHandler.async_step_reauth",
|
||||
return_value={
|
||||
"type": FlowResultType.FORM,
|
||||
"flow_id": "mock_flow",
|
||||
"step_id": "reauth_confirm",
|
||||
},
|
||||
) as mock_async_step_reauth,
|
||||
with patch(
|
||||
"homeassistant.components.synology_dsm.SynoApi.async_setup",
|
||||
side_effect=SynologyDSMLoginInvalidException(USERNAME),
|
||||
):
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
@@ -85,7 +75,8 @@ async def test_reauth_triggered(hass: HomeAssistant) -> None:
|
||||
entry.add_to_hass(hass)
|
||||
assert not await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
mock_async_step_reauth.assert_called_once()
|
||||
assert entry.state is ConfigEntryState.SETUP_ERROR
|
||||
assert any(entry.async_get_active_flows(hass, {SOURCE_REAUTH}))
|
||||
|
||||
|
||||
async def test_config_entry_migrations(
|
||||
|
||||
@@ -124,6 +124,7 @@ def mock_weheat_heat_pump_instance() -> MagicMock:
|
||||
mock_heat_pump_instance.energy_in_dhw = 6789
|
||||
mock_heat_pump_instance.energy_in_defrost = 555
|
||||
mock_heat_pump_instance.energy_in_cooling = 9000
|
||||
mock_heat_pump_instance.energy_in_standby = 684
|
||||
mock_heat_pump_instance.energy_total = 28689
|
||||
mock_heat_pump_instance.energy_out_heating = 10000
|
||||
mock_heat_pump_instance.energy_out_dhw = 6677
|
||||
|
||||
@@ -877,6 +877,64 @@
|
||||
'state': '12345',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.test_model_electricity_used_standby-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
None,
|
||||
]),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'config_subentry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.test_model_electricity_used_standby',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'object_id_base': 'Electricity used standby',
|
||||
'options': dict({
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 2,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENERGY: 'energy'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Electricity used standby',
|
||||
'platform': 'weheat',
|
||||
'previous_unique_id': None,
|
||||
'suggested_object_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'electricity_used_standby',
|
||||
'unique_id': '0000-1111-2222-3333_electricity_used_standby',
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.test_model_electricity_used_standby-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'energy',
|
||||
'friendly_name': 'Test Model Electricity used standby',
|
||||
'state_class': <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
'unit_of_measurement': <UnitOfEnergy.KILO_WATT_HOUR: 'kWh'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.test_model_electricity_used_standby',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '684',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.test_model_energy_output_cooling-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': list([
|
||||
|
||||
@@ -33,7 +33,7 @@ async def test_all_entities(
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("has_dhw", "nr_of_entities"), [(False, 22), (True, 27)])
|
||||
@pytest.mark.parametrize(("has_dhw", "nr_of_entities"), [(False, 23), (True, 28)])
|
||||
async def test_create_entities(
|
||||
hass: HomeAssistant,
|
||||
mock_weheat_discover: AsyncMock,
|
||||
|
||||
Reference in New Issue
Block a user