Compare commits

..

2 Commits

Author SHA1 Message Date
epenet
642ffa45c3 Merge branch 'dev' into drop-ignore-missing-annotations 2025-10-16 12:09:42 +02:00
epenet
1bfac54e56 Drop ignore-missing-annotations from pylint 2025-08-10 17:28:33 +02:00
210 changed files with 882 additions and 22523 deletions

View File

@@ -326,7 +326,7 @@ jobs:
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Install Cosign
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
with:
cosign-release: "v2.2.3"

View File

@@ -689,14 +689,14 @@ jobs:
run: |
. venv/bin/activate
python --version
pylint --ignore-missing-annotations=y homeassistant
pylint homeassistant
- name: Run pylint (partially)
if: needs.info.outputs.test_full_suite == 'false'
shell: bash
run: |
. venv/bin/activate
python --version
pylint --ignore-missing-annotations=y homeassistant/components/${{ needs.info.outputs.integrations_glob }}
pylint homeassistant/components/${{ needs.info.outputs.integrations_glob }}
pylint-tests:
name: Check pylint on tests

2
CODEOWNERS generated
View File

@@ -619,8 +619,6 @@ build.json @home-assistant/supervisor
/tests/components/greeneye_monitor/ @jkeljo
/homeassistant/components/group/ @home-assistant/core
/tests/components/group/ @home-assistant/core
/homeassistant/components/growatt_server/ @johanzander
/tests/components/growatt_server/ @johanzander
/homeassistant/components/guardian/ @bachya
/tests/components/guardian/ @bachya
/homeassistant/components/habitica/ @tr4nt0r

View File

@@ -1,10 +1,10 @@
image: ghcr.io/home-assistant/{arch}-homeassistant
build_from:
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.10.1
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.10.1
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.10.1
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.10.1
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.10.1
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.10.0
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.10.0
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.10.0
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.10.0
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.10.0
codenotary:
signer: notary@home-assistant.io
base_image: notary@home-assistant.io

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any, Final, final
from typing import Final, final
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
@@ -133,9 +133,9 @@ class AirQualityEntity(Entity):
@final
@property
def state_attributes(self) -> dict[str, Any]:
def state_attributes(self) -> dict[str, str | int | float]:
"""Return the state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
data: dict[str, str | int | float] = {}
for prop, attr in PROP_TO_ATTR.items():
if (value := getattr(self, prop)) is not None:

View File

@@ -29,7 +29,7 @@
},
"data_description": {
"return_average": "air-Q allows to poll both the noisy sensor readings as well as the values averaged on the device (default)",
"clip_negatives": "For baseline calibration purposes, certain sensor values may briefly become negative. The default behavior is to clip such values to 0"
"clip_negatives": "For baseline calibration purposes, certain sensor values may briefly become negative. The default behaviour is to clip such values to 0"
}
}
}

View File

@@ -301,12 +301,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
@property
def state_attributes(self) -> dict[str, Any] | None:
"""Return the state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
data[ATTR_CODE_FORMAT] = self.code_format
data[ATTR_CHANGED_BY] = self.changed_by
data[ATTR_CODE_ARM_REQUIRED] = self.code_arm_required
return data
return {
ATTR_CODE_FORMAT: self.code_format,
ATTR_CHANGED_BY: self.changed_by,
ATTR_CODE_ARM_REQUIRED: self.code_arm_required,
}
async def async_internal_added_to_hass(self) -> None:
"""Call when the alarm control panel entity is added to hass."""

View File

@@ -41,8 +41,6 @@ from .pipeline import (
async_setup_pipeline_store,
async_update_pipeline,
)
from .select import AssistPipelineSelect, VadSensitivitySelect
from .vad import VadSensitivity
from .websocket_api import async_register_websocket_api
__all__ = (
@@ -53,14 +51,11 @@ __all__ = (
"SAMPLE_CHANNELS",
"SAMPLE_RATE",
"SAMPLE_WIDTH",
"AssistPipelineSelect",
"AudioSettings",
"Pipeline",
"PipelineEvent",
"PipelineEventType",
"PipelineNotFound",
"VadSensitivity",
"VadSensitivitySelect",
"WakeWordSettings",
"async_create_default_pipeline",
"async_get_pipelines",

View File

@@ -72,16 +72,7 @@ class WrtDevice(NamedTuple):
_LOGGER = logging.getLogger(__name__)
type _FuncType[_T] = Callable[
[_T],
Awaitable[
list[str]
| tuple[float | None, float | None]
| list[float]
| dict[str, float | str | None]
| dict[str, float]
],
]
type _FuncType[_T] = Callable[[_T], Awaitable[list[Any] | tuple[Any] | dict[str, Any]]]
type _ReturnFuncType[_T] = Callable[[_T], Coroutine[Any, Any, dict[str, Any]]]
@@ -96,9 +87,7 @@ def handle_errors_and_zip[_AsusWrtBridgeT: AsusWrtBridge](
"""Run library methods and zip results or manage exceptions."""
@functools.wraps(func)
async def _wrapper(
self: _AsusWrtBridgeT,
) -> dict[str, float | str | None] | dict[str, float]:
async def _wrapper(self: _AsusWrtBridgeT) -> dict[str, str]:
try:
data = await func(self)
except exceptions as exc:
@@ -324,22 +313,22 @@ class AsusWrtLegacyBridge(AsusWrtBridge):
return [SENSORS_TEMPERATURES_LEGACY[i] for i in range(3) if availability[i]]
@handle_errors_and_zip((IndexError, OSError, ValueError), SENSORS_BYTES)
async def _get_bytes(self) -> tuple[float | None, float | None]:
async def _get_bytes(self) -> Any:
"""Fetch byte information from the router."""
return await self._api.async_get_bytes_total()
@handle_errors_and_zip((IndexError, OSError, ValueError), SENSORS_RATES)
async def _get_rates(self) -> tuple[float, float]:
async def _get_rates(self) -> Any:
"""Fetch rates information from the router."""
return await self._api.async_get_current_transfer_rates()
@handle_errors_and_zip((IndexError, OSError, ValueError), SENSORS_LOAD_AVG)
async def _get_load_avg(self) -> list[float]:
async def _get_load_avg(self) -> Any:
"""Fetch load average information from the router."""
return await self._api.async_get_loadavg()
@handle_errors_and_zip((OSError, ValueError), None)
async def _get_temperatures(self) -> dict[str, float]:
async def _get_temperatures(self) -> Any:
"""Fetch temperatures information from the router."""
return await self._api.async_get_temperature()

View File

@@ -146,7 +146,7 @@
},
"state": {
"title": "Add a Bayesian sensor",
"description": "Add an observation which evaluates to `True` when the value of the sensor exactly matches *'To state'*. When `False`, it will update the prior with probabilities that are the inverse of those set below. This behavior can be overridden by adding observations for the same entity's other states.",
"description": "Add an observation which evaluates to `True` when the value of the sensor exactly matches *'To state'*. When `False`, it will update the prior with probabilities that are the inverse of those set below. This behaviour can be overridden by adding observations for the same entity's other states.",
"data": {
"name": "[%key:common::config_flow::data::name%]",

View File

@@ -113,6 +113,7 @@ __all__ = [
"BluetoothServiceInfo",
"BluetoothServiceInfoBleak",
"HaBluetoothConnector",
"HomeAssistantRemoteScanner",
"async_address_present",
"async_ble_device_from_address",
"async_clear_address_from_match_history",

View File

@@ -525,18 +525,17 @@ class CalendarEntity(Entity):
@property
def state_attributes(self) -> dict[str, Any] | None:
"""Return the entity state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
if (event := self.event) is None:
return data or None
return None
data["message"] = event.summary
data["all_day"] = event.all_day
data["start_time"] = event.start_datetime_local.strftime(DATE_STR_FORMAT)
data["end_time"] = event.end_datetime_local.strftime(DATE_STR_FORMAT)
data["location"] = event.location if event.location else ""
data["description"] = event.description if event.description else ""
return data
return {
"message": event.summary,
"all_day": event.all_day,
"start_time": event.start_datetime_local.strftime(DATE_STR_FORMAT),
"end_time": event.end_datetime_local.strftime(DATE_STR_FORMAT),
"location": event.location if event.location else "",
"description": event.description if event.description else "",
}
@final
@property

View File

@@ -74,10 +74,7 @@ from .const import (
StreamType,
)
from .helper import get_camera_from_entity_id
from .img_util import (
TurboJPEGSingleton, # noqa: F401
scale_jpeg_camera_image,
)
from .img_util import scale_jpeg_camera_image
from .prefs import (
CameraPreferences,
DynamicStreamSettings, # noqa: F401
@@ -664,9 +661,7 @@ class Camera(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def state_attributes(self) -> dict[str, str | None]:
"""Return the camera state attributes."""
attrs: dict[str, Any] = self.generate_entity_state_attributes()
attrs["access_token"] = self.access_tokens[-1]
attrs = {"access_token": self.access_tokens[-1]}
if model := self.model:
attrs["model_name"] = model

View File

@@ -341,16 +341,16 @@ class ClimateEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the optional state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
supported_features = self.supported_features
temperature_unit = self.temperature_unit
precision = self.precision
hass = self.hass
data[ATTR_CURRENT_TEMPERATURE] = show_temp(
data: dict[str, str | float | None] = {
ATTR_CURRENT_TEMPERATURE: show_temp(
hass, self.current_temperature, temperature_unit, precision
)
),
}
if ClimateEntityFeature.TARGET_TEMPERATURE in supported_features:
data[ATTR_TEMPERATURE] = show_temp(

View File

@@ -19,7 +19,7 @@ from homeassistant.components.alexa import (
errors as alexa_errors,
smart_home as alexa_smart_home,
)
from homeassistant.components.camera import async_register_ice_servers
from homeassistant.components.camera.webrtc import async_register_ice_servers
from homeassistant.components.google_assistant import smart_home as ga
from homeassistant.const import __version__ as HA_VERSION
from homeassistant.core import Context, HassJob, HomeAssistant, callback

View File

@@ -12,9 +12,7 @@ from hass_nabucasa.google_report_state import ErrorResponse
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.components.google_assistant import DOMAIN as GOOGLE_DOMAIN
from homeassistant.components.google_assistant.helpers import ( # pylint: disable=hass-component-root-import
AbstractConfig,
)
from homeassistant.components.google_assistant.helpers import AbstractConfig
from homeassistant.components.homeassistant.exposed_entities import (
async_expose_entity,
async_get_assistant_settings,

View File

@@ -13,6 +13,6 @@
"integration_type": "system",
"iot_class": "cloud_push",
"loggers": ["acme", "hass_nabucasa", "snitun"],
"requirements": ["hass-nabucasa==1.4.0"],
"requirements": ["hass-nabucasa==1.3.0"],
"single_config_entry": true
}

View File

@@ -11,7 +11,7 @@ from hass_nabucasa.voice import MAP_VOICE, Gender
from homeassistant.auth.const import GROUP_ID_ADMIN
from homeassistant.auth.models import User
from homeassistant.components import webhook
from homeassistant.components.google_assistant.http import ( # pylint: disable=hass-component-root-import
from homeassistant.components.google_assistant.http import (
async_get_users as async_get_google_assistant_users,
)
from homeassistant.core import HomeAssistant, callback

View File

@@ -6,9 +6,7 @@ from typing import Any
import uuid
from homeassistant.components.automation import DOMAIN as AUTOMATION_DOMAIN
from homeassistant.components.automation.config import ( # pylint: disable=hass-component-root-import
async_validate_config_item,
)
from homeassistant.components.automation.config import async_validate_config_item
from homeassistant.config import AUTOMATION_CONFIG_PATH
from homeassistant.const import CONF_ID, SERVICE_RELOAD
from homeassistant.core import HomeAssistant, callback

View File

@@ -5,9 +5,7 @@ from __future__ import annotations
from typing import Any
from homeassistant.components.script import DOMAIN as SCRIPT_DOMAIN
from homeassistant.components.script.config import ( # pylint: disable=hass-component-root-import
async_validate_config_item,
)
from homeassistant.components.script.config import async_validate_config_item
from homeassistant.config import SCRIPT_CONFIG_PATH
from homeassistant.const import SERVICE_RELOAD
from homeassistant.core import HomeAssistant, callback

View File

@@ -569,13 +569,10 @@ class ChatLog:
if llm_api:
prompt_parts.append(llm_api.api_prompt)
# Append current date and time to the prompt if the corresponding tool is not provided
llm_tools: list[llm.Tool] = llm_api.tools if llm_api else []
if not any(tool.name.endswith("GetDateTime") for tool in llm_tools):
prompt_parts.append(
await self._async_expand_prompt_template(
llm_context,
llm.DATE_TIME_PROMPT,
llm.BASE_PROMPT,
llm_context.language,
user_name,
)

View File

@@ -267,7 +267,7 @@ class CoverEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
data = {}
if (current := self.current_cover_position) is not None:
data[ATTR_CURRENT_POSITION] = current

View File

@@ -5,9 +5,7 @@ from __future__ import annotations
import datetime
from homeassistant.components.alarm_control_panel import AlarmControlPanelState
from homeassistant.components.manual.alarm_control_panel import ( # pylint: disable=hass-component-root-import
ManualAlarm,
)
from homeassistant.components.manual.alarm_control_panel import ManualAlarm
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ARMING_TIME, CONF_DELAY_TIME, CONF_TRIGGER_TIME
from homeassistant.core import HomeAssistant

View File

@@ -139,7 +139,6 @@ class DemoCover(CoverEntity):
self.async_write_ha_state()
return
self._is_opening = False
self._is_closing = True
self._listen_cover()
self._requested_closing = True
@@ -163,7 +162,6 @@ class DemoCover(CoverEntity):
return
self._is_opening = True
self._is_closing = False
self._listen_cover()
self._requested_closing = False
self.async_write_ha_state()
@@ -183,14 +181,10 @@ class DemoCover(CoverEntity):
if self._position == position:
return
self._is_closing = position < (self._position or 0)
self._is_opening = not self._is_closing
self._listen_cover()
self._requested_closing = (
self._position is not None and position < self._position
)
self.async_write_ha_state()
async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
"""Move the cover til to a specific position."""

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
import asyncio
from typing import Any, final
from typing import final
from propcache.api import cached_property
@@ -28,6 +28,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity_platform import EntityPlatform
from homeassistant.helpers.typing import StateType
from homeassistant.util.hass_dict import HassKey
from .const import (
@@ -188,11 +189,9 @@ class BaseTrackerEntity(Entity):
raise NotImplementedError
@property
def state_attributes(self) -> dict[str, Any]:
def state_attributes(self) -> dict[str, StateType]:
"""Return the device state attributes."""
attr: dict[str, Any] = self.generate_entity_state_attributes()
attr[ATTR_SOURCE_TYPE] = self.source_type
attr: dict[str, StateType] = {ATTR_SOURCE_TYPE: self.source_type}
if self.battery_level is not None:
attr[ATTR_BATTERY_LEVEL] = self.battery_level
@@ -279,9 +278,9 @@ class TrackerEntity(
@final
@property
def state_attributes(self) -> dict[str, Any]:
def state_attributes(self) -> dict[str, StateType]:
"""Return the device state attributes."""
attr: dict[str, Any] = {}
attr: dict[str, StateType] = {}
attr.update(super().state_attributes)
if self.latitude is not None and self.longitude is not None:
@@ -432,10 +431,9 @@ class ScannerEntity(
@final
@property
def state_attributes(self) -> dict[str, Any]:
def state_attributes(self) -> dict[str, StateType]:
"""Return the device state attributes."""
attr: dict[str, Any] = self.generate_entity_state_attributes()
attr.update(super().state_attributes)
attr = super().state_attributes
if ip_address := self.ip_address:
attr[ATTR_IP] = ip_address

View File

@@ -48,7 +48,7 @@ from homeassistant.helpers.event import (
async_track_utc_time_change,
)
from homeassistant.helpers.restore_state import RestoreEntity
from homeassistant.helpers.typing import ConfigType, GPSType
from homeassistant.helpers.typing import ConfigType, GPSType, StateType
from homeassistant.setup import (
SetupPhases,
async_notify_setup_error,
@@ -842,11 +842,9 @@ class Device(RestoreEntity):
@final
@property
def state_attributes(self) -> dict[str, Any]:
def state_attributes(self) -> dict[str, StateType]:
"""Return the device state attributes."""
attributes: dict[str, Any] = self.generate_entity_state_attributes()
attributes[ATTR_SOURCE_TYPE] = self.source_type
attributes: dict[str, StateType] = {ATTR_SOURCE_TYPE: self.source_type}
if self.gps is not None:
attributes[ATTR_LATITUDE] = self.gps[0]

View File

@@ -6,5 +6,5 @@
"iot_class": "local_polling",
"loggers": ["pydoods"],
"quality_scale": "legacy",
"requirements": ["pydoods==1.0.2", "Pillow==12.0.0"]
"requirements": ["pydoods==1.0.2", "Pillow==11.3.0"]
}

View File

@@ -8,11 +8,8 @@ from eheimdigital.classic_vario import EheimDigitalClassicVario
from eheimdigital.device import EheimDigitalDevice
from eheimdigital.types import FilterErrorCode
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.components.sensor.const import SensorDeviceClass
from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfTime
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

View File

@@ -16,9 +16,7 @@ from homeassistant.components.sensor import (
SensorEntity,
SensorStateClass,
)
from homeassistant.components.sensor.recorder import ( # pylint: disable=hass-component-root-import
reset_detected,
)
from homeassistant.components.sensor.recorder import reset_detected
from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, UnitOfEnergy, UnitOfVolume
from homeassistant.core import (
HomeAssistant,

View File

@@ -10,8 +10,8 @@ from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.components.sensor.const import SensorStateClass
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

View File

@@ -6,7 +6,7 @@ from dataclasses import replace
from aioesphomeapi import EntityInfo, SelectInfo, SelectState
from homeassistant.components.assist_pipeline import (
from homeassistant.components.assist_pipeline.select import (
AssistPipelineSelect,
VadSensitivitySelect,
)

View File

@@ -180,9 +180,7 @@ class EventEntity(RestoreEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_)
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
attributes: dict[str, Any] = self.generate_entity_state_attributes()
attributes[ATTR_EVENT_TYPE] = self.__last_event_type
attributes = {ATTR_EVENT_TYPE: self.__last_event_type}
if last_event_attributes := self.__last_event_attributes:
attributes |= last_event_attributes
return attributes

View File

@@ -385,10 +385,9 @@ class FanEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@final
@property
def state_attributes(self) -> dict[str, Any]:
def state_attributes(self) -> dict[str, float | str | None]:
"""Return optional state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
data: dict[str, float | str | None] = {}
supported_features = self.supported_features
if FanEntityFeature.DIRECTION in supported_features:

View File

@@ -19,9 +19,7 @@ from homeassistant.components.ffmpeg import (
FFmpegManager,
get_ffmpeg_manager,
)
from homeassistant.components.ffmpeg_motion.binary_sensor import ( # pylint: disable=hass-component-root-import
FFmpegBinarySensor,
)
from homeassistant.components.ffmpeg_motion.binary_sensor import FFmpegBinarySensor
from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv

View File

@@ -4,12 +4,8 @@ from __future__ import annotations
from pyfirefly.models import Account, Category
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
StateType,
)
from homeassistant.components.sensor import SensorEntity, SensorStateClass, StateType
from homeassistant.components.sensor.const import SensorDeviceClass
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

View File

@@ -6,8 +6,9 @@ import logging
from typing import Any
from homeassistant.components.camera import CameraEntityFeature
from homeassistant.components.ffmpeg import CONF_EXTRA_ARGUMENTS, CONF_INPUT
from homeassistant.components.ffmpeg.camera import ( # pylint: disable=hass-component-root-import
from homeassistant.components.ffmpeg.camera import (
CONF_EXTRA_ARGUMENTS,
CONF_INPUT,
DEFAULT_ARGUMENTS,
FFmpegCamera,
)

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/generic",
"integration_type": "device",
"iot_class": "local_push",
"requirements": ["av==13.1.0", "Pillow==12.0.0"]
"requirements": ["av==13.1.0", "Pillow==11.3.0"]
}

View File

@@ -101,9 +101,7 @@ class GeolocationEvent(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of this external event."""
data: dict[str, Any] = self.generate_entity_state_attributes()
data[ATTR_SOURCE] = self.source
data: dict[str, Any] = {ATTR_SOURCE: self.source}
if self.latitude is not None:
data[ATTR_LATITUDE] = round(self.latitude, 5)
if self.longitude is not None:

View File

@@ -30,8 +30,8 @@ from homeassistant.components.camera import (
WebRTCMessage,
WebRTCSendMessage,
async_register_webrtc_provider,
get_dynamic_camera_stream_settings,
)
from homeassistant.components.camera.prefs import get_dynamic_camera_stream_settings
from homeassistant.components.default_config import DOMAIN as DEFAULT_CONFIG_DOMAIN
from homeassistant.components.stream import Orientation
from homeassistant.config_entries import SOURCE_SYSTEM, ConfigEntry

View File

@@ -1,7 +1,7 @@
{
"domain": "growatt_server",
"name": "Growatt",
"codeowners": ["@johanzander"],
"codeowners": [],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/growatt_server",
"iot_class": "cloud_polling",

View File

@@ -7,5 +7,5 @@
"iot_class": "cloud_polling",
"loggers": ["habiticalib"],
"quality_scale": "platinum",
"requirements": ["habiticalib==0.4.6"]
"requirements": ["habiticalib==0.4.5"]
}

View File

@@ -27,7 +27,7 @@ from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
)
from homeassistant.components.camera import DOMAIN as CAMERA_DOMAIN
from homeassistant.components.device_automation.trigger import ( # pylint: disable=hass-component-root-import
from homeassistant.components.device_automation.trigger import (
async_validate_trigger_config,
)
from homeassistant.components.event import DOMAIN as EVENT_DOMAIN, EventDeviceClass

View File

@@ -188,7 +188,7 @@ class HumidifierEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_AT
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the optional state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
data: dict[str, Any] = {}
if self.action is not None:
data[ATTR_ACTION] = self.action if self.is_on else HumidifierAction.OFF

View File

@@ -5,7 +5,6 @@
"config_flow": true,
"dependencies": ["application_credentials"],
"documentation": "https://www.home-assistant.io/integrations/husqvarna_automower",
"integration_type": "hub",
"iot_class": "cloud_push",
"loggers": ["aioautomower"],
"quality_scale": "silver",

View File

@@ -12,5 +12,5 @@
"dependencies": ["bluetooth_adapters"],
"documentation": "https://www.home-assistant.io/integrations/husqvarna_automower_ble",
"iot_class": "local_polling",
"requirements": ["automower-ble==0.2.8", "gardena-bluetooth==1.6.0"]
"requirements": ["automower-ble==0.2.7", "gardena-bluetooth==1.6.0"]
}

View File

@@ -10,7 +10,7 @@ from datetime import datetime, timedelta
import logging
import os
from random import SystemRandom
from typing import Any, Final, final
from typing import Final, final
from aiohttp import hdrs, web
import httpx
@@ -281,12 +281,9 @@ class ImageEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@final
@property
def state_attributes(self) -> dict[str, Any]:
def state_attributes(self) -> dict[str, str | None]:
"""Return the state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
data["access_token"] = self.access_tokens[-1]
return data
return {"access_token": self.access_tokens[-1]}
@callback
def async_update_token(self) -> None:

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/image_upload",
"integration_type": "system",
"quality_scale": "internal",
"requirements": ["Pillow==12.0.0"]
"requirements": ["Pillow==11.3.0"]
}

View File

@@ -2,12 +2,7 @@
from typing import Any, Final
from iometer import (
IOmeterClient,
IOmeterConnectionError,
IOmeterNoReadingsError,
IOmeterNoStatusError,
)
from iometer import IOmeterClient, IOmeterConnectionError
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
@@ -39,11 +34,6 @@ class IOMeterConfigFlow(ConfigFlow, domain=DOMAIN):
client = IOmeterClient(host=host, session=session)
try:
status = await client.get_current_status()
_ = await client.get_current_reading()
except IOmeterNoStatusError:
return self.async_abort(reason="no_status")
except IOmeterNoReadingsError:
return self.async_abort(reason="no_readings")
except IOmeterConnectionError:
return self.async_abort(reason="cannot_connect")
@@ -80,11 +70,6 @@ class IOMeterConfigFlow(ConfigFlow, domain=DOMAIN):
client = IOmeterClient(host=self._host, session=session)
try:
status = await client.get_current_status()
_ = await client.get_current_reading()
except IOmeterNoStatusError:
errors["base"] = "no_status"
except IOmeterNoReadingsError:
errors["base"] = "no_readings"
except IOmeterConnectionError:
errors["base"] = "cannot_connect"
else:

View File

@@ -20,8 +20,6 @@
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]"
},
"error": {
"no_status": "No status received from the IOmeter. Check your device status in the IOmeter app",
"no_readings": "No readings received from the IOmeter. Please attach the IOmeter Core to the electricity meter and wait for the first reading.",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]"
}

View File

@@ -31,7 +31,6 @@ class IPPEntity(CoordinatorEntity[IPPDataUpdateCoordinator]):
manufacturer=self.coordinator.data.info.manufacturer,
model=self.coordinator.data.info.model,
name=self.coordinator.data.info.name,
serial_number=self.coordinator.data.info.serial,
sw_version=self.coordinator.data.info.version,
configuration_url=self.coordinator.data.info.more_info,
)

View File

@@ -358,7 +358,7 @@
"entity_label": "Entity name",
"entity_description": "Optional if a device is selected, otherwise required. If the entity is assigned to a device, the device name is used as prefix.",
"entity_category_title": "Entity category",
"entity_category_description": "Classification of a non-primary entity. Leave empty for standard behavior."
"entity_category_description": "Classification of a non-primary entity. Leave empty for standard behaviour."
},
"knx": {
"title": "KNX configuration",

View File

@@ -1261,8 +1261,7 @@ class LightEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def state_attributes(self) -> dict[str, Any] | None:
"""Return state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
data: dict[str, Any] = {}
supported_features = self.supported_features_compat
supported_color_modes = self.supported_color_modes
legacy_supported_color_modes = (

View File

@@ -25,7 +25,7 @@ from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.typing import ConfigType, StateType
from homeassistant.util.hass_dict import HassKey
from .const import DOMAIN, LockState
@@ -244,10 +244,9 @@ class LockEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@final
@property
def state_attributes(self) -> dict[str, Any]:
def state_attributes(self) -> dict[str, StateType]:
"""Return the state attributes."""
state_attr: dict[str, Any] = self.generate_entity_state_attributes()
state_attr = {}
for prop, attr in PROP_TO_ATTR.items():
if (value := getattr(self, prop)) is not None:
state_attr[attr] = value

View File

@@ -8,7 +8,7 @@ from pychromecast import Chromecast
from pychromecast.const import CAST_TYPE_CHROMECAST
from homeassistant.components.cast import DOMAIN as CAST_DOMAIN
from homeassistant.components.cast.home_assistant_cast import ( # pylint: disable=hass-component-root-import
from homeassistant.components.cast.home_assistant_cast import (
ATTR_URL_PATH,
ATTR_VIEW_PATH,
NO_URL_AVAILABLE_ERROR,

View File

@@ -6,5 +6,5 @@
"iot_class": "cloud_push",
"loggers": ["matrix_client"],
"quality_scale": "legacy",
"requirements": ["matrix-nio==0.25.2", "Pillow==12.0.0"]
"requirements": ["matrix-nio==0.25.2", "Pillow==11.3.0"]
}

View File

@@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "local_polling",
"quality_scale": "platinum",
"requirements": ["aiomealie==1.0.1"]
"requirements": ["aiomealie==1.0.0"]
}

View File

@@ -1123,7 +1123,7 @@ class MediaPlayerEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
state_attr: dict[str, Any] = self.generate_entity_state_attributes()
state_attr: dict[str, Any] = {}
if self.support_grouping:
state_attr[ATTR_GROUP_MEMBERS] = self.group_members

View File

@@ -5,8 +5,9 @@ from __future__ import annotations
import asyncio
import logging
from datapoint.Forecast import Forecast
from datapoint.Manager import Manager
import datapoint
import datapoint.Forecast
import datapoint.Manager
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
@@ -47,19 +48,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
coordinates = f"{latitude}_{longitude}"
connection = Manager(api_key=api_key)
connection = datapoint.Manager.Manager(api_key=api_key)
async def async_update_hourly() -> Forecast:
async def async_update_hourly() -> datapoint.Forecast:
return await hass.async_add_executor_job(
fetch_data, connection, latitude, longitude, "hourly"
)
async def async_update_daily() -> Forecast:
async def async_update_daily() -> datapoint.Forecast:
return await hass.async_add_executor_job(
fetch_data, connection, latitude, longitude, "daily"
)
async def async_update_twice_daily() -> Forecast:
async def async_update_twice_daily() -> datapoint.Forecast:
return await hass.async_add_executor_job(
fetch_data, connection, latitude, longitude, "twice-daily"
)

View File

@@ -6,8 +6,9 @@ from collections.abc import Mapping
import logging
from typing import Any
import datapoint
from datapoint.exceptions import APIException
from datapoint.Manager import Manager
import datapoint.Manager
from requests import HTTPError
import voluptuous as vol
@@ -30,7 +31,7 @@ async def validate_input(
Data has the keys from DATA_SCHEMA with values provided by the user.
"""
errors = {}
connection = Manager(api_key=api_key)
connection = datapoint.Manager.Manager(api_key=api_key)
try:
forecast = await hass.async_add_executor_job(

View File

@@ -5,9 +5,8 @@ from __future__ import annotations
import logging
from typing import Any, Literal
from datapoint.exceptions import APIException
import datapoint
from datapoint.Forecast import Forecast
from datapoint.Manager import Manager
from requests import HTTPError
from homeassistant.exceptions import ConfigEntryAuthFailed
@@ -17,7 +16,7 @@ _LOGGER = logging.getLogger(__name__)
def fetch_data(
connection: Manager,
connection: datapoint.Manager,
latitude: float,
longitude: float,
frequency: Literal["daily", "twice-daily", "hourly"],
@@ -27,7 +26,7 @@ def fetch_data(
return connection.get_forecast(
latitude, longitude, frequency, convert_weather_code=False
)
except (ValueError, APIException) as err:
except (ValueError, datapoint.exceptions.APIException) as err:
_LOGGER.error("Check Met Office connection: %s", err.args)
raise UpdateFailed from err
except HTTPError as err:

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
from datetime import datetime
from typing import Any, cast
from datapoint.Forecast import Forecast
from datapoint.Forecast import Forecast as ForecastData
from homeassistant.components.weather import (
ATTR_FORECAST_CONDITION,
@@ -22,7 +22,7 @@ from homeassistant.components.weather import (
ATTR_FORECAST_WIND_BEARING,
DOMAIN as WEATHER_DOMAIN,
CoordinatorWeatherEntity,
Forecast as WeatherForecast,
Forecast,
WeatherEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
@@ -85,20 +85,20 @@ async def async_setup_entry(
)
def _build_hourly_forecast_data(timestep: dict[str, Any]) -> WeatherForecast:
data = WeatherForecast(datetime=timestep["time"].isoformat())
def _build_hourly_forecast_data(timestep: dict[str, Any]) -> Forecast:
data = Forecast(datetime=timestep["time"].isoformat())
_populate_forecast_data(data, timestep, HOURLY_FORECAST_ATTRIBUTE_MAP)
return data
def _build_daily_forecast_data(timestep: dict[str, Any]) -> WeatherForecast:
data = WeatherForecast(datetime=timestep["time"].isoformat())
def _build_daily_forecast_data(timestep: dict[str, Any]) -> Forecast:
data = Forecast(datetime=timestep["time"].isoformat())
_populate_forecast_data(data, timestep, DAILY_FORECAST_ATTRIBUTE_MAP)
return data
def _build_twice_daily_forecast_data(timestep: dict[str, Any]) -> WeatherForecast:
data = WeatherForecast(datetime=timestep["time"].isoformat())
def _build_twice_daily_forecast_data(timestep: dict[str, Any]) -> Forecast:
data = Forecast(datetime=timestep["time"].isoformat())
# day and night forecasts have slightly different format
if "daySignificantWeatherCode" in timestep:
@@ -111,7 +111,7 @@ def _build_twice_daily_forecast_data(timestep: dict[str, Any]) -> WeatherForecas
def _populate_forecast_data(
forecast: WeatherForecast, timestep: dict[str, Any], mapping: dict[str, str]
forecast: Forecast, timestep: dict[str, Any], mapping: dict[str, str]
) -> None:
def get_mapped_attribute(attr: str) -> Any:
if attr not in mapping:
@@ -153,9 +153,9 @@ def _populate_forecast_data(
class MetOfficeWeather(
CoordinatorWeatherEntity[
TimestampDataUpdateCoordinator[Forecast],
TimestampDataUpdateCoordinator[Forecast],
TimestampDataUpdateCoordinator[Forecast],
TimestampDataUpdateCoordinator[ForecastData],
TimestampDataUpdateCoordinator[ForecastData],
TimestampDataUpdateCoordinator[ForecastData],
]
):
"""Implementation of a Met Office weather condition."""
@@ -177,9 +177,9 @@ class MetOfficeWeather(
def __init__(
self,
coordinator_daily: TimestampDataUpdateCoordinator[Forecast],
coordinator_hourly: TimestampDataUpdateCoordinator[Forecast],
coordinator_twice_daily: TimestampDataUpdateCoordinator[Forecast],
coordinator_daily: TimestampDataUpdateCoordinator[ForecastData],
coordinator_hourly: TimestampDataUpdateCoordinator[ForecastData],
coordinator_twice_daily: TimestampDataUpdateCoordinator[ForecastData],
hass_data: dict[str, Any],
) -> None:
"""Initialise the platform with a data instance."""
@@ -263,10 +263,10 @@ class MetOfficeWeather(
return float(value) if value is not None else None
@callback
def _async_forecast_daily(self) -> list[WeatherForecast] | None:
def _async_forecast_daily(self) -> list[Forecast] | None:
"""Return the daily forecast in native units."""
coordinator = cast(
TimestampDataUpdateCoordinator[Forecast],
TimestampDataUpdateCoordinator[ForecastData],
self.forecast_coordinators["daily"],
)
timesteps = coordinator.data.timesteps
@@ -277,10 +277,10 @@ class MetOfficeWeather(
]
@callback
def _async_forecast_hourly(self) -> list[WeatherForecast] | None:
def _async_forecast_hourly(self) -> list[Forecast] | None:
"""Return the hourly forecast in native units."""
coordinator = cast(
TimestampDataUpdateCoordinator[Forecast],
TimestampDataUpdateCoordinator[ForecastData],
self.forecast_coordinators["hourly"],
)
@@ -292,10 +292,10 @@ class MetOfficeWeather(
]
@callback
def _async_forecast_twice_daily(self) -> list[WeatherForecast] | None:
def _async_forecast_twice_daily(self) -> list[Forecast] | None:
"""Return the twice daily forecast in native units."""
coordinator = cast(
TimestampDataUpdateCoordinator[Forecast],
TimestampDataUpdateCoordinator[ForecastData],
self.forecast_coordinators["twice_daily"],
)
timesteps = coordinator.data.timesteps

View File

@@ -8,7 +8,6 @@ from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from .onewirehub import OneWireConfigEntry
@@ -27,28 +26,7 @@ async def async_get_config_entry_diagnostics(
"data": async_redact_data(entry.data, TO_REDACT),
"options": {**entry.options},
},
"devices": [asdict(device_details) for device_details in onewire_hub.devices],
}
async def async_get_device_diagnostics(
hass: HomeAssistant, entry: OneWireConfigEntry, device_entry: dr.DeviceEntry
) -> dict[str, Any]:
"""Return diagnostics for a device."""
onewire_hub = entry.runtime_data
return {
"entry": {
"title": entry.title,
"data": async_redact_data(entry.data, TO_REDACT),
"options": {**entry.options},
},
"device": asdict(
next(
device_details
for device_details in onewire_hub.devices
if device_details.id[3:] == device_entry.serial_number
)
),
"devices": [asdict(device_details) for device_details in onewire_hub.devices]
if onewire_hub.devices
else [],
}

View File

@@ -7,7 +7,6 @@
"integration_type": "hub",
"iot_class": "local_polling",
"loggers": ["aio_ownet"],
"quality_scale": "silver",
"requirements": ["aio-ownet==0.0.4"],
"zeroconf": ["_owserver._tcp.local."]
}

View File

@@ -1,6 +1,8 @@
rules:
## Bronze
config-flow: done
config-flow:
status: todo
comment: missing data_description on options flow
test-before-configure: done
unique-config-entry:
status: done
@@ -14,19 +16,27 @@ rules:
entity-event-setup:
status: exempt
comment: entities do not subscribe to events
dependency-transparency: done
dependency-transparency:
status: todo
comment: The package is not built and published inside a CI pipeline
action-setup:
status: exempt
comment: No service actions currently available
common-modules:
status: done
comment: base entity available, but no coordinator
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: done
docs-high-level-description:
status: todo
comment: Under review
docs-installation-instructions:
status: todo
comment: Under review
docs-removal-instructions:
status: todo
comment: Under review
docs-actions:
status: exempt
comment: No service actions currently available
status: todo
comment: Under review
brands: done
## Silver
@@ -42,8 +52,12 @@ rules:
parallel-updates: done
test-coverage: done
integration-owner: done
docs-installation-parameters: done
docs-configuration-parameters: done
docs-installation-parameters:
status: todo
comment: Under review
docs-configuration-parameters:
status: todo
comment: Under review
## Gold
entity-translations: done
@@ -59,7 +73,9 @@ rules:
comment: >
Manual removal, as it is not possible to distinguish
between a flaky device and a device that has been removed
diagnostics: done
diagnostics:
status: todo
comment: config-entry diagnostics level available, might be nice to have device-level diagnostics
exception-translations:
status: todo
comment: Under review

View File

@@ -139,12 +139,8 @@
"step": {
"device_selection": {
"data": {
"clear_device_options": "Reset all device customizations",
"device_selection": "Customize specific devices"
},
"data_description": {
"clear_device_options": "Use this to reset all device specific options to default values.",
"device_selection": "Customize behavior of individual devices."
"clear_device_options": "Clear all device configurations",
"device_selection": "[%key:component::onewire::options::error::device_not_selected%]"
},
"description": "Select what configuration steps to process",
"title": "1-Wire device options"
@@ -153,9 +149,6 @@
"data": {
"precision": "Sensor precision"
},
"data_description": {
"precision": "The lower the precision, the faster the sensor will respond, but with less accuracy."
},
"description": "Select sensor precision for {sensor_id}",
"title": "1-Wire sensor precision"
}

View File

@@ -70,7 +70,7 @@ async def async_setup_entry(
vol.Optional(ATTR_TILT): vol.In([DIR_UP, DIR_DOWN]),
vol.Optional(ATTR_ZOOM): vol.In([ZOOM_OUT, ZOOM_IN]),
vol.Optional(ATTR_DISTANCE, default=0.1): cv.small_float,
vol.Optional(ATTR_SPEED): cv.small_float,
vol.Optional(ATTR_SPEED, default=0.5): cv.small_float,
vol.Optional(ATTR_MOVE_MODE, default=RELATIVE_MOVE): vol.In(
[
CONTINUOUS_MOVE,
@@ -210,10 +210,10 @@ class ONVIFCameraEntity(ONVIFBaseEntity, Camera):
async def async_perform_ptz(
self,
distance,
speed,
move_mode,
continuous_duration,
preset,
speed=None,
pan=None,
tilt=None,
zoom=None,

View File

@@ -602,7 +602,6 @@ class ONVIFDevice:
return
req.PresetToken = preset_val
if speed_val is not None:
req.Speed = {
"PanTilt": {"x": speed_val, "y": speed_val},
"Zoom": {"x": speed_val},

View File

@@ -30,6 +30,7 @@ ptz:
max: 1
step: 0.01
speed:
default: 0.5
selector:
number:
min: 0

View File

@@ -25,13 +25,6 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
}
)
STEP_RECONFIGURE_DATA_SCHEMA = vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_PORT): cv.port,
}
)
async def validate_input(hass: HomeAssistant, host: str, port: int) -> None:
"""Validate the user input allows us to connect."""
@@ -46,48 +39,6 @@ async def validate_input(hass: HomeAssistant, host: str, port: int) -> None:
class OpenRGBConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for OpenRGB."""
async def async_step_reconfigure(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reconfiguration of the OpenRGB SDK Server."""
reconfigure_entry = self._get_reconfigure_entry()
errors: dict[str, str] = {}
if user_input is not None:
host = user_input[CONF_HOST]
port = user_input[CONF_PORT]
# Prevent duplicate entries
self._async_abort_entries_match({CONF_HOST: host, CONF_PORT: port})
try:
await validate_input(self.hass, host, port)
except CONNECTION_ERRORS:
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception(
"Unknown error while connecting to OpenRGB SDK server at %s",
f"{host}:{port}",
)
errors["base"] = "unknown"
else:
return self.async_update_reload_and_abort(
reconfigure_entry,
data_updates={CONF_HOST: host, CONF_PORT: port},
)
return self.async_show_form(
step_id="reconfigure",
data_schema=self.add_suggested_values_to_schema(
data_schema=STEP_RECONFIGURE_DATA_SCHEMA,
suggested_values={
CONF_HOST: reconfigure_entry.data[CONF_HOST],
CONF_PORT: reconfigure_entry.data[CONF_PORT],
},
),
errors=errors,
)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:

View File

@@ -68,7 +68,7 @@ rules:
entity-translations: todo
exception-translations: done
icon-translations: todo
reconfiguration-flow: done
reconfiguration-flow: todo
repair-issues: todo
stale-devices: todo

View File

@@ -2,7 +2,6 @@
"config": {
"step": {
"user": {
"title": "Set up OpenRGB SDK server",
"description": "Set up your OpenRGB SDK server to allow control from within Home Assistant.",
"data": {
"name": "[%key:common::config_flow::data::name%]",
@@ -14,18 +13,6 @@
"host": "The IP address or hostname of the computer running the OpenRGB SDK server.",
"port": "The port number that the OpenRGB SDK server is running on."
}
},
"reconfigure": {
"title": "Reconfigure OpenRGB SDK server",
"description": "Update the connection settings for your OpenRGB SDK server.",
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"data_description": {
"host": "[%key:component::openrgb::config::step::user::data_description::host%]",
"port": "[%key:component::openrgb::config::step::user::data_description::port%]"
}
}
},
"error": {
@@ -34,7 +21,6 @@
"unknown": "[%key:common::config_flow::error::unknown%]"
},
"abort": {
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
}
},

View File

@@ -8,6 +8,6 @@
"iot_class": "local_polling",
"loggers": ["plugwise"],
"quality_scale": "platinum",
"requirements": ["plugwise==1.8.1"],
"requirements": ["plugwise==1.8.0"],
"zeroconf": ["_plugwise._tcp.local."]
}

View File

@@ -4,5 +4,5 @@
"codeowners": [],
"documentation": "https://www.home-assistant.io/integrations/proxy",
"quality_scale": "legacy",
"requirements": ["Pillow==12.0.0"]
"requirements": ["Pillow==11.3.0"]
}

View File

@@ -6,5 +6,5 @@
"iot_class": "calculated",
"loggers": ["pyzbar"],
"quality_scale": "legacy",
"requirements": ["Pillow==12.0.0", "pyzbar==0.1.7"]
"requirements": ["Pillow==11.3.0", "pyzbar==0.1.7"]
}

View File

@@ -72,7 +72,7 @@ class RAPTPillConfigFlow(ConfigFlow, domain=DOMAIN):
title=self._discovered_devices[address], data={}
)
current_addresses = self._async_current_ids(include_ignore=False)
current_addresses = self._async_current_ids()
for discovery_info in async_discovered_service_info(self.hass, False):
address = discovery_info.address
if address in current_addresses or address in self._discovered_devices:

View File

@@ -184,14 +184,13 @@ class RemoteEntity(ToggleEntity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_)
@property
def state_attributes(self) -> dict[str, Any] | None:
"""Return optional state attributes."""
data: dict[str, Any] = self.generate_entity_state_attributes()
if RemoteEntityFeature.ACTIVITY not in self.supported_features:
return data or None
return None
data[ATTR_ACTIVITY_LIST] = self.activity_list
data[ATTR_CURRENT_ACTIVITY] = self.current_activity
return data
return {
ATTR_ACTIVITY_LIST: self.activity_list,
ATTR_CURRENT_ACTIVITY: self.current_activity,
}
def send_command(self, command: Iterable[str], **kwargs: Any) -> None:
"""Send commands to a device."""

View File

@@ -437,8 +437,6 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
@override
def state_attributes(self) -> dict[str, Any] | None:
"""Return state attributes."""
state_attr: dict[str, Any] = self.generate_entity_state_attributes()
if last_reset := self.last_reset:
state_class = self.state_class
if state_class != SensorStateClass.TOTAL:
@@ -450,9 +448,9 @@ class SensorEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_):
)
if state_class == SensorStateClass.TOTAL:
state_attr[ATTR_LAST_RESET] = last_reset.isoformat()
return {ATTR_LAST_RESET: last_reset.isoformat()}
return state_attr or None
return None
@cached_property
def native_value(self) -> StateType | date | datetime | Decimal:

View File

@@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/seven_segments",
"iot_class": "local_polling",
"quality_scale": "legacy",
"requirements": ["Pillow==12.0.0"]
"requirements": ["Pillow==11.3.0"]
}

View File

@@ -59,8 +59,8 @@ from .coordinator import (
)
from .repairs import (
async_manage_ble_scanner_firmware_unsupported_issue,
async_manage_deprecated_firmware_issue,
async_manage_outbound_websocket_incorrectly_enabled_issue,
async_manage_wall_display_firmware_unsupported_issue,
)
from .utils import (
async_create_issue_unsupported_firmware,
@@ -337,7 +337,7 @@ async def _async_setup_rpc_entry(hass: HomeAssistant, entry: ShellyConfigEntry)
await hass.config_entries.async_forward_entry_setups(
entry, runtime_data.platforms
)
async_manage_deprecated_firmware_issue(hass, entry)
async_manage_wall_display_firmware_unsupported_issue(hass, entry)
async_manage_ble_scanner_firmware_unsupported_issue(
hass,
entry,

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
from enum import StrEnum
from logging import Logger, getLogger
import re
from typing import Final, TypedDict
from typing import Final
from aioshelly.const import (
MODEL_BULB,
@@ -232,6 +232,7 @@ class BLEScannerMode(StrEnum):
BLE_SCANNER_MIN_FIRMWARE = "1.5.1"
WALL_DISPLAY_MIN_FIRMWARE = "2.3.0"
MAX_PUSH_UPDATE_FAILURES = 5
PUSH_UPDATE_ISSUE_ID = "push_update_{unique}"
@@ -244,28 +245,9 @@ BLE_SCANNER_FIRMWARE_UNSUPPORTED_ISSUE_ID = "ble_scanner_firmware_unsupported_{u
OUTBOUND_WEBSOCKET_INCORRECTLY_ENABLED_ISSUE_ID = (
"outbound_websocket_incorrectly_enabled_{unique}"
)
DEPRECATED_FIRMWARE_ISSUE_ID = "deprecated_firmware_{unique}"
class DeprecatedFirmwareInfo(TypedDict):
"""TypedDict for Deprecated Firmware Info."""
min_firmware: str
ha_version: str
# Provide firmware deprecation data:
# key: device model
# value: dict with:
# min_firmware: minimum supported firmware version
# ha_version: Home Assistant version when older firmware will be deprecated
# Example:
# DEPRECATED_FIRMWARES: dict[str, DeprecatedFirmwareInfo] = {
# MODEL_WALL_DISPLAY: DeprecatedFirmwareInfo(
# {"min_firmware": "2.3.0", "ha_version": "2025.10.0"}
# ),
# }
DEPRECATED_FIRMWARES: dict[str, DeprecatedFirmwareInfo] = {}
WALL_DISPLAY_FIRMWARE_UNSUPPORTED_ISSUE_ID = (
"wall_display_firmware_unsupported_{unique}"
)
GAS_VALVE_OPEN_STATES = ("opening", "opened")

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from aioshelly.const import MODEL_OUT_PLUG_S_G3, MODEL_PLUG_S_G3
from aioshelly.const import MODEL_OUT_PLUG_S_G3, MODEL_PLUG_S_G3, MODEL_WALL_DISPLAY
from aioshelly.exceptions import DeviceConnectionError, RpcCallError
from aioshelly.rpc_device import RpcDevice
from awesomeversion import AwesomeVersion
@@ -19,10 +19,10 @@ from .const import (
BLE_SCANNER_FIRMWARE_UNSUPPORTED_ISSUE_ID,
BLE_SCANNER_MIN_FIRMWARE,
CONF_BLE_SCANNER_MODE,
DEPRECATED_FIRMWARE_ISSUE_ID,
DEPRECATED_FIRMWARES,
DOMAIN,
OUTBOUND_WEBSOCKET_INCORRECTLY_ENABLED_ISSUE_ID,
WALL_DISPLAY_FIRMWARE_UNSUPPORTED_ISSUE_ID,
WALL_DISPLAY_MIN_FIRMWARE,
BLEScannerMode,
)
from .coordinator import ShellyConfigEntry
@@ -70,25 +70,21 @@ def async_manage_ble_scanner_firmware_unsupported_issue(
@callback
def async_manage_deprecated_firmware_issue(
def async_manage_wall_display_firmware_unsupported_issue(
hass: HomeAssistant,
entry: ShellyConfigEntry,
) -> None:
"""Manage deprecated firmware issue."""
issue_id = DEPRECATED_FIRMWARE_ISSUE_ID.format(unique=entry.unique_id)
"""Manage the Wall Display firmware unsupported issue."""
issue_id = WALL_DISPLAY_FIRMWARE_UNSUPPORTED_ISSUE_ID.format(unique=entry.unique_id)
if TYPE_CHECKING:
assert entry.runtime_data.rpc is not None
device = entry.runtime_data.rpc.device
model = entry.data["model"]
if model in DEPRECATED_FIRMWARES:
min_firmware = DEPRECATED_FIRMWARES[model]["min_firmware"]
ha_version = DEPRECATED_FIRMWARES[model]["ha_version"]
if entry.data["model"] == MODEL_WALL_DISPLAY:
firmware = AwesomeVersion(device.shelly["ver"])
if firmware < min_firmware:
if firmware < WALL_DISPLAY_MIN_FIRMWARE:
ir.async_create_issue(
hass,
DOMAIN,
@@ -96,12 +92,11 @@ def async_manage_deprecated_firmware_issue(
is_fixable=True,
is_persistent=True,
severity=ir.IssueSeverity.WARNING,
translation_key="deprecated_firmware",
translation_key="wall_display_firmware_unsupported",
translation_placeholders={
"device_name": device.name,
"ip_address": device.ip_address,
"firmware": firmware,
"ha_version": ha_version,
},
data={"entry_id": entry.entry_id},
)
@@ -246,7 +241,7 @@ async def async_create_fix_flow(
if (
"ble_scanner_firmware_unsupported" in issue_id
or "deprecated_firmware" in issue_id
or "wall_display_firmware_unsupported" in issue_id
):
return FirmwareUpdateFlow(device)

View File

@@ -312,13 +312,13 @@
}
}
},
"deprecated_firmware": {
"wall_display_firmware_unsupported": {
"title": "{device_name} is running outdated firmware",
"fix_flow": {
"step": {
"confirm": {
"title": "{device_name} is running outdated firmware",
"description": "Your Shelly device {device_name} with IP address {ip_address} is running firmware {firmware}. This firmware version will not be supported by Shelly integration starting from Home Assistant {ha_version}.\n\nSelect **Submit** button to start the OTA update to the latest stable firmware version."
"description": "Your Shelly device {device_name} with IP address {ip_address} is running firmware {firmware}. This firmware version will not be supported by Shelly integration starting from Home Assistant 2025.11.0.\n\nSelect **Submit** button to start the OTA update to the latest stable firmware version."
}
},
"abort": {

View File

@@ -6,5 +6,5 @@
"iot_class": "cloud_polling",
"loggers": ["simplehound"],
"quality_scale": "legacy",
"requirements": ["Pillow==12.0.0", "simplehound==0.3"]
"requirements": ["Pillow==11.3.0", "simplehound==0.3"]
}

View File

@@ -39,9 +39,7 @@ from homeassistant.components.media_player import (
async_process_play_media_url,
)
from homeassistant.components.plex import PLEX_URI_SCHEME
from homeassistant.components.plex.services import ( # pylint: disable=hass-component-root-import
process_plex_payload,
)
from homeassistant.components.plex.services import process_plex_payload
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import entity_registry as er

View File

@@ -441,7 +441,9 @@ class KeyFrameConverter:
# Keep import here so that we can import stream integration
# without installing reqs
from homeassistant.components.camera import TurboJPEGSingleton # noqa: PLC0415
from homeassistant.components.camera.img_util import ( # noqa: PLC0415
TurboJPEGSingleton,
)
self._packet: Packet | None = None
self._event: asyncio.Event = asyncio.Event()

View File

@@ -7,7 +7,7 @@ import io
import logging
from ssl import SSLContext
from types import MappingProxyType
from typing import Any, cast
from typing import Any
import httpx
from telegram import (
@@ -23,7 +23,6 @@ from telegram import (
InputMediaVideo,
InputPollOption,
Message,
PhotoSize,
ReplyKeyboardMarkup,
ReplyKeyboardRemove,
Update,
@@ -57,10 +56,6 @@ from .const import (
ATTR_DISABLE_NOTIF,
ATTR_DISABLE_WEB_PREV,
ATTR_FILE,
ATTR_FILE_ID,
ATTR_FILE_MIME_TYPE,
ATTR_FILE_NAME,
ATTR_FILE_SIZE,
ATTR_FROM_FIRST,
ATTR_FROM_LAST,
ATTR_INLINE_MESSAGE_ID,
@@ -91,7 +86,6 @@ from .const import (
CONF_CHAT_ID,
CONF_PROXY_URL,
DOMAIN,
EVENT_TELEGRAM_ATTACHMENT,
EVENT_TELEGRAM_CALLBACK,
EVENT_TELEGRAM_COMMAND,
EVENT_TELEGRAM_SENT,
@@ -189,10 +183,6 @@ class BaseTelegramBot:
# This is a command message - set event type to command and split data into command and args
event_type = EVENT_TELEGRAM_COMMAND
event_data.update(self._get_command_event_data(message.text))
elif filters.ATTACHMENT.filter(message):
event_type = EVENT_TELEGRAM_ATTACHMENT
event_data[ATTR_TEXT] = message.caption
event_data.update(self._get_file_id_event_data(message))
else:
event_type = EVENT_TELEGRAM_TEXT
event_data[ATTR_TEXT] = message.text
@@ -202,26 +192,6 @@ class BaseTelegramBot:
return event_type, event_data
def _get_file_id_event_data(self, message: Message) -> dict[str, Any]:
"""Extract file_id from a message attachment, if any."""
if filters.PHOTO.filter(message):
photos = cast(Sequence[PhotoSize], message.effective_attachment)
return {
ATTR_FILE_ID: photos[-1].file_id,
ATTR_FILE_MIME_TYPE: "image/jpeg", # telegram always uses jpeg for photos
ATTR_FILE_SIZE: photos[-1].file_size,
}
return {
k: getattr(message.effective_attachment, v)
for k, v in (
(ATTR_FILE_ID, "file_id"),
(ATTR_FILE_NAME, "file_name"),
(ATTR_FILE_MIME_TYPE, "mime_type"),
(ATTR_FILE_SIZE, "file_size"),
)
if hasattr(message.effective_attachment, v)
}
def _get_user_event_data(self, user: User) -> dict[str, Any]:
return {
ATTR_USER_ID: user.id,
@@ -578,7 +548,6 @@ class TelegramNotificationService:
"Error sending message",
params[ATTR_MESSAGE_TAG],
text,
target=target,
parse_mode=params[ATTR_PARSER],
disable_web_page_preview=params[ATTR_DISABLE_WEB_PREV],
disable_notification=params[ATTR_DISABLE_NOTIF],

View File

@@ -54,7 +54,6 @@ SERVICE_LEAVE_CHAT = "leave_chat"
EVENT_TELEGRAM_CALLBACK = "telegram_callback"
EVENT_TELEGRAM_COMMAND = "telegram_command"
EVENT_TELEGRAM_TEXT = "telegram_text"
EVENT_TELEGRAM_ATTACHMENT = "telegram_attachment"
EVENT_TELEGRAM_SENT = "telegram_sent"
PARSER_HTML = "html"
@@ -91,10 +90,6 @@ ATTR_DISABLE_NOTIF = "disable_notification"
ATTR_DISABLE_WEB_PREV = "disable_web_page_preview"
ATTR_EDITED_MSG = "edited_message"
ATTR_FILE = "file"
ATTR_FILE_ID = "file_id"
ATTR_FILE_MIME_TYPE = "file_mime_type"
ATTR_FILE_NAME = "file_name"
ATTR_FILE_SIZE = "file_size"
ATTR_FROM_FIRST = "from_first"
ATTR_FROM_LAST = "from_last"
ATTR_KEYBOARD = "keyboard"

View File

@@ -21,9 +21,7 @@ from homeassistant.components.sensor import (
SensorEntity,
SensorStateClass,
)
from homeassistant.components.sensor.helpers import ( # pylint: disable=hass-component-root-import
async_parse_date_datetime,
)
from homeassistant.components.sensor.helpers import async_parse_date_datetime
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_ENTITY_ID,

View File

@@ -11,6 +11,6 @@
"tf-models-official==2.5.0",
"pycocotools==2.0.6",
"numpy==2.3.2",
"Pillow==12.0.0"
"Pillow==11.3.0"
]
}

View File

@@ -433,8 +433,6 @@ class UpdateEntity(
@property
def state_attributes(self) -> dict[str, Any] | None:
"""Return state attributes."""
state_attr: dict[str, Any] = self.generate_entity_state_attributes()
if (release_summary := self.release_summary) is not None:
release_summary = release_summary[:255]
@@ -461,17 +459,18 @@ class UpdateEntity(
skipped_version = None
self.__skipped_version = None
state_attr[ATTR_AUTO_UPDATE] = self.auto_update
state_attr[ATTR_DISPLAY_PRECISION] = self.display_precision
state_attr[ATTR_INSTALLED_VERSION] = installed_version
state_attr[ATTR_IN_PROGRESS] = in_progress
state_attr[ATTR_LATEST_VERSION] = latest_version
state_attr[ATTR_RELEASE_SUMMARY] = release_summary
state_attr[ATTR_RELEASE_URL] = self.release_url
state_attr[ATTR_SKIPPED_VERSION] = skipped_version
state_attr[ATTR_TITLE] = self.title
state_attr[ATTR_UPDATE_PERCENTAGE] = update_percentage
return state_attr
return {
ATTR_AUTO_UPDATE: self.auto_update,
ATTR_DISPLAY_PRECISION: self.display_precision,
ATTR_INSTALLED_VERSION: installed_version,
ATTR_IN_PROGRESS: in_progress,
ATTR_LATEST_VERSION: latest_version,
ATTR_RELEASE_SUMMARY: release_summary,
ATTR_RELEASE_URL: self.release_url,
ATTR_SKIPPED_VERSION: skipped_version,
ATTR_TITLE: self.title,
ATTR_UPDATE_PERCENTAGE: update_percentage,
}
@final
async def async_install_with_progress(

View File

@@ -20,9 +20,7 @@ from homeassistant.components.sensor import (
SensorExtraStoredData,
SensorStateClass,
)
from homeassistant.components.sensor.recorder import ( # pylint: disable=hass-component-root-import
_suggest_report_issue,
)
from homeassistant.components.sensor.recorder import _suggest_report_issue
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_DEVICE_CLASS,

View File

@@ -364,12 +364,10 @@ class StateVacuumEntity(
"""Get the list of available fan speed steps of the vacuum cleaner."""
return self._attr_fan_speed_list
@final
@property
def state_attributes(self) -> dict[str, Any]:
"""Return the state attributes of the vacuum cleaner."""
data: dict[str, Any] = self.generate_entity_state_attributes()
data: dict[str, Any] = {}
supported_features = self.supported_features
if VacuumEntityFeature.BATTERY in supported_features:

View File

@@ -191,11 +191,9 @@ class ValveEntity(Entity):
@property
def state_attributes(self) -> dict[str, Any] | None:
"""Return the state attributes."""
state_attr: dict[str, Any] = self.generate_entity_state_attributes()
if self.reports_position:
state_attr[ATTR_CURRENT_POSITION] = self.current_valve_position
return state_attr or None
if not self.reports_position:
return None
return {ATTR_CURRENT_POSITION: self.current_valve_position}
@property
def supported_features(self) -> ValveEntityFeature:

View File

@@ -13,5 +13,5 @@
"documentation": "https://www.home-assistant.io/integrations/vesync",
"iot_class": "cloud_polling",
"loggers": ["pyvesync"],
"requirements": ["pyvesync==3.1.2"]
"requirements": ["pyvesync==3.1.0"]
}

View File

@@ -24,7 +24,6 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
@@ -118,32 +117,6 @@ GLOBAL_SENSORS: tuple[ViCareBinarySensorEntityDescription, ...] = (
device_class=BinarySensorDeviceClass.PROBLEM,
value_getter=lambda api: len(api.getDeviceErrors()) > 0,
),
ViCareBinarySensorEntityDescription(
key="identification_mode",
translation_key="identification_mode",
entity_category=EntityCategory.DIAGNOSTIC,
value_getter=lambda api: api.getIdentification(),
entity_registry_enabled_default=False,
),
ViCareBinarySensorEntityDescription(
key="mounting_mode",
translation_key="mounting_mode",
entity_category=EntityCategory.DIAGNOSTIC,
value_getter=lambda api: api.getMountingMode(),
entity_registry_enabled_default=False,
),
ViCareBinarySensorEntityDescription(
key="child_safety_lock_mode",
translation_key="child_safety_lock_mode",
value_getter=lambda api: api.getChildLock() == "active",
entity_registry_enabled_default=False,
),
ViCareBinarySensorEntityDescription(
key="valve",
translation_key="valve",
device_class=BinarySensorDeviceClass.DOOR,
value_getter=lambda api: api.isValveOpen(),
),
)

View File

@@ -21,15 +21,6 @@
},
"one_time_charge": {
"default": "mdi:shower-head"
},
"mounting_mode": {
"default": "mdi:wrench"
},
"child_safety_lock_mode": {
"default": "mdi:lock"
},
"valve": {
"default": "mdi:pipe-valve"
}
},
"button": {
@@ -99,12 +90,6 @@
},
"ventilation_level": {
"default": "mdi:fan"
},
"zigbee_signal_strength": {
"default": "mdi:wifi"
},
"valve_position": {
"default": "mdi:pipe-valve"
}
}
},

View File

@@ -943,23 +943,6 @@ GLOBAL_SENSORS: tuple[ViCareSensorEntityDescription, ...] = (
entity_category=EntityCategory.DIAGNOSTIC,
value_getter=lambda api: api.getBatteryLevel(),
),
ViCareSensorEntityDescription(
key="zigbee_signal_strength",
translation_key="zigbee_signal_strength",
state_class=SensorStateClass.MEASUREMENT,
entity_category=EntityCategory.DIAGNOSTIC,
native_unit_of_measurement=PERCENTAGE,
value_getter=lambda api: api.getZigbeeSignalStrength(),
entity_registry_enabled_default=False,
),
ViCareSensorEntityDescription(
key="valve_position",
translation_key="valve_position",
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
value_getter=lambda api: api.getValvePosition(),
entity_registry_enabled_default=False,
),
ViCareSensorEntityDescription(
key="fuel_need",
translation_key="fuel_need",

View File

@@ -66,18 +66,6 @@
},
"one_time_charge": {
"name": "One-time charge"
},
"identification_mode": {
"name": "Identification mode"
},
"mounting_mode": {
"name": "Mounting mode"
},
"child_safety_lock_mode": {
"name": "Child safety lock"
},
"valve": {
"name": "Valve"
}
},
"button": {
@@ -514,12 +502,6 @@
},
"fuel_need": {
"name": "Fuel need"
},
"zigbee_signal_strength": {
"name": "[%key:component::sensor::entity_component::signal_strength::name%]"
},
"valve_position": {
"name": "Valve position"
}
},
"water_heater": {

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
from homeassistant.components.assist_pipeline.repair_flows import ( # pylint: disable=hass-component-root-import
from homeassistant.components.assist_pipeline.repair_flows import (
AssistInProgressDeprecatedRepairFlow,
)
from homeassistant.components.repairs import RepairsFlow

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from homeassistant.components.assist_pipeline import (
from homeassistant.components.assist_pipeline.select import (
AssistPipelineSelect,
VadSensitivitySelect,
)

Some files were not shown because too many files have changed in this diff Show More