Compare commits

..

4 Commits

Author SHA1 Message Date
Jan Čermák
7a1e378300 Sleep 1e-99s in async_block_till_done to workaround unfinished tasks 2026-02-05 18:08:07 +01:00
Jan Čermák
f1e1792e64 Revert "Try reverting changes from Python's gh-105836 patch"
This reverts commit 5de171f714.
2026-02-05 16:56:18 +01:00
Jan Čermák
5de171f714 Try reverting changes from Python's gh-105836 patch 2026-02-05 14:09:09 +01:00
Jan Čermák
6b07b2b8bc Bump base image to 2026.02.0 with Python 3.14.3, use 3.14.3 in CI
This also bumps libcec used in the base image to 7.1.1, full changelog:
* https://github.com/home-assistant/docker/releases/tag/2026.02.0

Python changelog:
* https://docs.python.org/release/3.14.3/whatsnew/changelog.html
2026-02-05 08:59:14 +01:00
668 changed files with 2873 additions and 8217 deletions

View File

@@ -10,12 +10,12 @@ on:
env:
BUILD_TYPE: core
DEFAULT_PYTHON: "3.14.2"
DEFAULT_PYTHON: "3.14.3"
PIP_TIMEOUT: 60
UV_HTTP_TIMEOUT: 60
UV_SYSTEM_PYTHON: "true"
# Base image version from https://github.com/home-assistant/docker
BASE_IMAGE_VERSION: "2026.01.0"
BASE_IMAGE_VERSION: "2026.02.0"
ARCHITECTURES: '["amd64", "aarch64"]'
jobs:

View File

@@ -41,8 +41,8 @@ env:
UV_CACHE_VERSION: 1
MYPY_CACHE_VERSION: 1
HA_SHORT_VERSION: "2026.3"
DEFAULT_PYTHON: "3.14.2"
ALL_PYTHON_VERSIONS: "['3.14.2']"
DEFAULT_PYTHON: "3.14.3"
ALL_PYTHON_VERSIONS: "['3.14.3']"
# 10.3 is the oldest supported version
# - 10.3.32 is the version currently shipped with Synology (as of 17 Feb 2022)
# 10.6 is the current long-term-support

View File

@@ -24,11 +24,11 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Initialize CodeQL
uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
uses: github/codeql-action/init@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4.32.1
with:
languages: python
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
uses: github/codeql-action/analyze@6bc82e05fd0ea64601dd4b465378bbcf57de0314 # v4.32.1
with:
category: "/language:python"

View File

@@ -10,7 +10,7 @@ on:
- "**strings.json"
env:
DEFAULT_PYTHON: "3.14.2"
DEFAULT_PYTHON: "3.14.3"
jobs:
upload:

View File

@@ -17,7 +17,7 @@ on:
- "script/gen_requirements_all.py"
env:
DEFAULT_PYTHON: "3.14.2"
DEFAULT_PYTHON: "3.14.3"
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name}}

View File

@@ -221,7 +221,6 @@ homeassistant.components.generic_hygrostat.*
homeassistant.components.generic_thermostat.*
homeassistant.components.geo_location.*
homeassistant.components.geocaching.*
homeassistant.components.ghost.*
homeassistant.components.gios.*
homeassistant.components.github.*
homeassistant.components.glances.*
@@ -436,7 +435,6 @@ homeassistant.components.raspberry_pi.*
homeassistant.components.rdw.*
homeassistant.components.recollect_waste.*
homeassistant.components.recorder.*
homeassistant.components.redgtech.*
homeassistant.components.remember_the_milk.*
homeassistant.components.remote.*
homeassistant.components.remote_calendar.*

4
CODEOWNERS generated
View File

@@ -595,8 +595,6 @@ build.json @home-assistant/supervisor
/tests/components/geonetnz_quakes/ @exxamalte
/homeassistant/components/geonetnz_volcano/ @exxamalte
/tests/components/geonetnz_volcano/ @exxamalte
/homeassistant/components/ghost/ @johnonolan
/tests/components/ghost/ @johnonolan
/homeassistant/components/gios/ @bieniu
/tests/components/gios/ @bieniu
/homeassistant/components/github/ @timmo001 @ludeeus
@@ -1357,8 +1355,6 @@ build.json @home-assistant/supervisor
/tests/components/recorder/ @home-assistant/core
/homeassistant/components/recovery_mode/ @home-assistant/core
/tests/components/recovery_mode/ @home-assistant/core
/homeassistant/components/redgtech/ @jonhsady @luan-nvg
/tests/components/redgtech/ @jonhsady @luan-nvg
/homeassistant/components/refoss/ @ashionky
/tests/components/refoss/ @ashionky
/homeassistant/components/rehlko/ @bdraco @peterager

View File

@@ -64,7 +64,7 @@ class AbodeFlowHandler(ConfigFlow, domain=DOMAIN):
else:
errors = {"base": "cannot_connect"}
except ConnectTimeout, HTTPError:
except (ConnectTimeout, HTTPError):
errors = {"base": "cannot_connect"}
if errors:

View File

@@ -99,7 +99,7 @@ class AbodeLight(AbodeDevice, LightEntity):
return _hs
@property
def color_mode(self) -> ColorMode:
def color_mode(self) -> ColorMode | None:
"""Return the color mode of the light."""
if self._device.is_dimmable and self._device.is_color_capable:
if self.hs_color is not None:
@@ -110,7 +110,7 @@ class AbodeLight(AbodeDevice, LightEntity):
return ColorMode.ONOFF
@property
def supported_color_modes(self) -> set[ColorMode]:
def supported_color_modes(self) -> set[ColorMode] | None:
"""Flag supported color modes."""
if self._device.is_dimmable and self._device.is_color_capable:
return {ColorMode.COLOR_TEMP, ColorMode.HS}

View File

@@ -43,7 +43,7 @@ class AccuWeatherFlowHandler(ConfigFlow, domain=DOMAIN):
longitude=user_input[CONF_LONGITUDE],
)
await accuweather.async_get_location()
except ApiError, ClientConnectorError, TimeoutError, ClientError:
except (ApiError, ClientConnectorError, TimeoutError, ClientError):
errors["base"] = "cannot_connect"
except InvalidApiKeyError:
errors[CONF_API_KEY] = "invalid_api_key"
@@ -104,7 +104,7 @@ class AccuWeatherFlowHandler(ConfigFlow, domain=DOMAIN):
longitude=self._longitude,
)
await accuweather.async_get_location()
except ApiError, ClientConnectorError, TimeoutError, ClientError:
except (ApiError, ClientConnectorError, TimeoutError, ClientError):
errors["base"] = "cannot_connect"
except InvalidApiKeyError:
errors["base"] = "invalid_api_key"

View File

@@ -29,42 +29,30 @@ SWITCHES: tuple[ActronAirSwitchEntityDescription, ...] = (
key="away_mode",
translation_key="away_mode",
is_on_fn=lambda coordinator: coordinator.data.user_aircon_settings.away_mode,
set_fn=lambda coordinator, enabled: (
coordinator.data.user_aircon_settings.set_away_mode(enabled)
),
set_fn=lambda coordinator,
enabled: coordinator.data.user_aircon_settings.set_away_mode(enabled),
),
ActronAirSwitchEntityDescription(
key="continuous_fan",
translation_key="continuous_fan",
is_on_fn=lambda coordinator: (
coordinator.data.user_aircon_settings.continuous_fan_enabled
),
set_fn=lambda coordinator, enabled: (
coordinator.data.user_aircon_settings.set_continuous_mode(enabled)
),
is_on_fn=lambda coordinator: coordinator.data.user_aircon_settings.continuous_fan_enabled,
set_fn=lambda coordinator,
enabled: coordinator.data.user_aircon_settings.set_continuous_mode(enabled),
),
ActronAirSwitchEntityDescription(
key="quiet_mode",
translation_key="quiet_mode",
is_on_fn=lambda coordinator: (
coordinator.data.user_aircon_settings.quiet_mode_enabled
),
set_fn=lambda coordinator, enabled: (
coordinator.data.user_aircon_settings.set_quiet_mode(enabled)
),
is_on_fn=lambda coordinator: coordinator.data.user_aircon_settings.quiet_mode_enabled,
set_fn=lambda coordinator,
enabled: coordinator.data.user_aircon_settings.set_quiet_mode(enabled),
),
ActronAirSwitchEntityDescription(
key="turbo_mode",
translation_key="turbo_mode",
is_on_fn=lambda coordinator: (
coordinator.data.user_aircon_settings.turbo_enabled
),
set_fn=lambda coordinator, enabled: (
coordinator.data.user_aircon_settings.set_turbo_mode(enabled)
),
is_supported_fn=lambda coordinator: (
coordinator.data.user_aircon_settings.turbo_supported
),
is_on_fn=lambda coordinator: coordinator.data.user_aircon_settings.turbo_enabled,
set_fn=lambda coordinator,
enabled: coordinator.data.user_aircon_settings.set_turbo_mode(enabled),
is_supported_fn=lambda coordinator: coordinator.data.user_aircon_settings.turbo_supported,
),
)

View File

@@ -133,9 +133,8 @@ CONTROL_ENTITIES: tuple[AirGradientSelectEntityDescription, ...] = (
value_fn=lambda config: _get_value(
config.co2_automatic_baseline_calibration_days, ABC_DAYS
),
set_value_fn=lambda client, value: (
client.set_co2_automatic_baseline_calibration(int(value))
),
set_value_fn=lambda client,
value: client.set_co2_automatic_baseline_calibration(int(value)),
),
)

View File

@@ -85,7 +85,7 @@ class AirobotButton(AirobotEntity, ButtonEntity):
"""Handle the button press."""
try:
await self.entity_description.press_fn(self.coordinator)
except AirobotConnectionError, AirobotTimeoutError:
except (AirobotConnectionError, AirobotTimeoutError):
# Connection errors during reboot are expected as device restarts
pass
except AirobotError as err:

View File

@@ -114,7 +114,7 @@ class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
AirOSDeviceConnectionError,
):
self.errors["base"] = "cannot_connect"
except AirOSConnectionAuthenticationError, AirOSDataMissingError:
except (AirOSConnectionAuthenticationError, AirOSDataMissingError):
self.errors["base"] = "invalid_auth"
except AirOSKeyDataMissingError:
self.errors["base"] = "key_data_missing"

View File

@@ -130,7 +130,7 @@ class AirVisualFlowHandler(ConfigFlow, domain=DOMAIN):
try:
await coro
except InvalidKeyError, KeyExpiredError, UnauthorizedError:
except (InvalidKeyError, KeyExpiredError, UnauthorizedError):
errors[CONF_API_KEY] = "invalid_api_key"
except NotFoundError:
errors[CONF_CITY] = "location_not_found"

View File

@@ -100,7 +100,7 @@ class AirZoneCloudConfigFlow(ConfigFlow, domain=DOMAIN):
try:
await self.airzone.login()
except AirzoneCloudError, LoginError:
except (AirzoneCloudError, LoginError):
errors["base"] = "cannot_connect"
else:
return await self.async_step_inst_pick()

View File

@@ -123,7 +123,7 @@ class Auth:
allow_redirects=True,
)
except TimeoutError, aiohttp.ClientError:
except (TimeoutError, aiohttp.ClientError):
_LOGGER.error("Timeout calling LWA to get auth token")
return None

View File

@@ -358,7 +358,7 @@ async def async_send_changereport_message(
"""
try:
token = await config.async_get_access_token()
except RequireRelink, NoTokenAvailable:
except (RequireRelink, NoTokenAvailable):
await config.set_authorized(False)
_LOGGER.error(
"Error when sending ChangeReport to Alexa, could not get access token"
@@ -392,7 +392,7 @@ async def async_send_changereport_message(
allow_redirects=True,
)
except TimeoutError, aiohttp.ClientError:
except (TimeoutError, aiohttp.ClientError):
_LOGGER.error("Timeout sending report to Alexa for %s", alexa_entity.entity_id)
return
@@ -549,7 +549,7 @@ async def async_send_doorbell_event_message(
allow_redirects=True,
)
except TimeoutError, aiohttp.ClientError:
except (TimeoutError, aiohttp.ClientError):
_LOGGER.error("Timeout sending report to Alexa for %s", alexa_entity.entity_id)
return

View File

@@ -90,9 +90,6 @@
"cannot_retrieve_data_with_error": {
"message": "Error retrieving data: {error}"
},
"config_entry_not_found": {
"message": "Config entry not found: {device_id}"
},
"device_serial_number_missing": {
"message": "Device serial number missing: {device_id}"
},

View File

@@ -77,11 +77,9 @@ class AmbientNetworkConfigFlow(ConfigFlow, domain=DOMAIN):
# Filter out indoor stations
self._stations = dict(
filter(
lambda item: (
not item[1]
.get(API_STATION_INFO, {})
.get(API_STATION_INDOOR, False)
),
lambda item: not item[1]
.get(API_STATION_INFO, {})
.get(API_STATION_INDOOR, False),
self._stations.items(),
)
)
@@ -115,7 +113,7 @@ class AmbientNetworkConfigFlow(ConfigFlow, domain=DOMAIN):
)
return self.async_show_form(
step_id=CONF_USER, data_schema=schema, errors=errors or {}
step_id=CONF_USER, data_schema=schema, errors=errors if errors else {}
)
async def async_step_station(

View File

@@ -31,7 +31,7 @@ class AmbientStationFlowHandler(ConfigFlow, domain=DOMAIN):
return self.async_show_form(
step_id="user",
data_schema=self.data_schema,
errors=errors or {},
errors=errors if errors else {},
)
async def async_step_user(

View File

@@ -26,9 +26,10 @@ from homeassistant.const import (
UnitOfVolumetricFlux,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import AmbientStationConfigEntry
from . import AmbientStation, AmbientStationConfigEntry
from .const import ATTR_LAST_DATA, TYPE_SOLARRADIATION, TYPE_SOLARRADIATION_LX
from .entity import AmbientWeatherEntity
@@ -682,6 +683,22 @@ async def async_setup_entry(
class AmbientWeatherSensor(AmbientWeatherEntity, SensorEntity):
"""Define an Ambient sensor."""
def __init__(
self,
ambient: AmbientStation,
mac_address: str,
station_name: str,
description: EntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(ambient, mac_address, station_name, description)
if description.key == TYPE_SOLARRADIATION_LX:
# Since TYPE_SOLARRADIATION and TYPE_SOLARRADIATION_LX will have the same
# name in the UI, we influence the entity ID of TYPE_SOLARRADIATION_LX here
# to differentiate them:
self.entity_id = f"sensor.{station_name}_solar_rad_lx"
@callback
def update_from_latest_data(self) -> None:
"""Fetch new state data for the sensor."""

View File

@@ -93,7 +93,7 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
self._abort_if_unique_id_configured(updates={CONF_HOST: self.host})
try:
return await self._async_start_pair()
except CannotConnect, ConnectionClosed:
except (CannotConnect, ConnectionClosed):
errors["base"] = "cannot_connect"
else:
user_input = {}
@@ -135,7 +135,7 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
# Attempt to pair again.
try:
return await self._async_start_pair()
except CannotConnect, ConnectionClosed:
except (CannotConnect, ConnectionClosed):
# Device doesn't respond to the specified host. Abort.
# If we are in the user flow we could go back to the user step to allow
# them to enter a new IP address but we cannot do that for the zeroconf
@@ -203,7 +203,7 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
if user_input is not None:
try:
return await self._async_start_pair()
except CannotConnect, ConnectionClosed:
except (CannotConnect, ConnectionClosed):
# Device became network unreachable after discovery.
# Abort and let discovery find it again later.
return self.async_abort(reason="cannot_connect")
@@ -229,7 +229,7 @@ class AndroidTVRemoteConfigFlow(ConfigFlow, domain=DOMAIN):
if user_input is not None:
try:
return await self._async_start_pair()
except CannotConnect, ConnectionClosed:
except (CannotConnect, ConnectionClosed):
# Device is network unreachable. Abort.
errors["base"] = "cannot_connect"
return self.async_show_form(
@@ -264,7 +264,7 @@ class AndroidTVRemoteOptionsFlowHandler(OptionsFlowWithReload):
@callback
def _save_config(self, data: dict[str, Any]) -> ConfigFlowResult:
"""Save the updated options."""
new_data = {k: v for k, v in data.items() if k != CONF_APPS}
new_data = {k: v for k, v in data.items() if k not in [CONF_APPS]}
if self._apps:
new_data[CONF_APPS] = self._apps

View File

@@ -73,7 +73,7 @@ async def validate_account(auth: MSOB2CAuth, account_number: str) -> str | MSOB2
_aw = AnglianWater(authenticator=auth)
try:
await _aw.validate_smart_meter(account_number)
except InvalidAccountIdError, SmartMeterUnavailableError:
except (InvalidAccountIdError, SmartMeterUnavailableError):
return "smart_meter_unavailable"
return auth

View File

@@ -36,7 +36,6 @@ from homeassistant.helpers.selector import (
SelectOptionDict,
SelectSelector,
SelectSelectorConfig,
SelectSelectorMode,
TemplateSelector,
)
from homeassistant.helpers.typing import VolDictType
@@ -48,7 +47,6 @@ from .const import (
CONF_RECOMMENDED,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_WEB_SEARCH,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
@@ -60,7 +58,6 @@ from .const import (
DEFAULT_AI_TASK_NAME,
DEFAULT_CONVERSATION_NAME,
DOMAIN,
NON_ADAPTIVE_THINKING_MODELS,
NON_THINKING_MODELS,
WEB_SEARCH_UNSUPPORTED_MODELS,
)
@@ -114,7 +111,6 @@ async def get_model_list(client: anthropic.AsyncAnthropic) -> list[SelectOptionD
"claude-3-5-haiku-20241022",
"claude-3-opus-20240229",
)
and model_info.id[-2:-1] != "-"
else model_info.id
)
if short_form.search(model_alias):
@@ -358,9 +354,7 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
model = self.options[CONF_CHAT_MODEL]
if not model.startswith(tuple(NON_THINKING_MODELS)) and model.startswith(
tuple(NON_ADAPTIVE_THINKING_MODELS)
):
if not model.startswith(tuple(NON_THINKING_MODELS)):
step_schema[
vol.Optional(
CONF_THINKING_BUDGET, default=DEFAULT[CONF_THINKING_BUDGET]
@@ -377,22 +371,6 @@ class ConversationSubentryFlowHandler(ConfigSubentryFlow):
else:
self.options.pop(CONF_THINKING_BUDGET, None)
if not model.startswith(tuple(NON_ADAPTIVE_THINKING_MODELS)):
step_schema[
vol.Optional(
CONF_THINKING_EFFORT,
default=DEFAULT[CONF_THINKING_EFFORT],
)
] = SelectSelector(
SelectSelectorConfig(
options=["none", "low", "medium", "high", "max"],
translation_key=CONF_THINKING_EFFORT,
mode=SelectSelectorMode.DROPDOWN,
)
)
else:
self.options.pop(CONF_THINKING_EFFORT, None)
if not model.startswith(tuple(WEB_SEARCH_UNSUPPORTED_MODELS)):
step_schema.update(
{

View File

@@ -14,7 +14,6 @@ CONF_CHAT_MODEL = "chat_model"
CONF_MAX_TOKENS = "max_tokens"
CONF_TEMPERATURE = "temperature"
CONF_THINKING_BUDGET = "thinking_budget"
CONF_THINKING_EFFORT = "thinking_effort"
CONF_WEB_SEARCH = "web_search"
CONF_WEB_SEARCH_USER_LOCATION = "user_location"
CONF_WEB_SEARCH_MAX_USES = "web_search_max_uses"
@@ -30,7 +29,6 @@ DEFAULT = {
CONF_MAX_TOKENS: 3000,
CONF_TEMPERATURE: 1.0,
CONF_THINKING_BUDGET: 0,
CONF_THINKING_EFFORT: "low",
CONF_WEB_SEARCH: False,
CONF_WEB_SEARCH_USER_LOCATION: False,
CONF_WEB_SEARCH_MAX_USES: 5,
@@ -44,18 +42,6 @@ NON_THINKING_MODELS = [
"claude-3-haiku",
]
NON_ADAPTIVE_THINKING_MODELS = [
"claude-opus-4-5",
"claude-sonnet-4-5",
"claude-haiku-4-5",
"claude-opus-4-1",
"claude-opus-4-0",
"claude-opus-4-20250514",
"claude-sonnet-4-0",
"claude-sonnet-4-20250514",
"claude-3",
]
WEB_SEARCH_UNSUPPORTED_MODELS = [
"claude-3-haiku",
"claude-3-opus",

View File

@@ -23,7 +23,6 @@ from anthropic.types import (
MessageDeltaUsage,
MessageParam,
MessageStreamEvent,
OutputConfigParam,
RawContentBlockDeltaEvent,
RawContentBlockStartEvent,
RawContentBlockStopEvent,
@@ -42,7 +41,6 @@ from anthropic.types import (
TextDelta,
ThinkingBlock,
ThinkingBlockParam,
ThinkingConfigAdaptiveParam,
ThinkingConfigDisabledParam,
ThinkingConfigEnabledParam,
ThinkingDelta,
@@ -80,7 +78,6 @@ from .const import (
CONF_MAX_TOKENS,
CONF_TEMPERATURE,
CONF_THINKING_BUDGET,
CONF_THINKING_EFFORT,
CONF_WEB_SEARCH,
CONF_WEB_SEARCH_CITY,
CONF_WEB_SEARCH_COUNTRY,
@@ -92,7 +89,6 @@ from .const import (
DOMAIN,
LOGGER,
MIN_THINKING_BUDGET,
NON_ADAPTIVE_THINKING_MODELS,
NON_THINKING_MODELS,
)
@@ -626,34 +622,21 @@ class AnthropicBaseLLMEntity(Entity):
stream=True,
)
if not model.startswith(tuple(NON_ADAPTIVE_THINKING_MODELS)):
thinking_effort = options.get(
CONF_THINKING_EFFORT, DEFAULT[CONF_THINKING_EFFORT]
thinking_budget = options.get(
CONF_THINKING_BUDGET, DEFAULT[CONF_THINKING_BUDGET]
)
if (
not model.startswith(tuple(NON_THINKING_MODELS))
and thinking_budget >= MIN_THINKING_BUDGET
):
model_args["thinking"] = ThinkingConfigEnabledParam(
type="enabled", budget_tokens=thinking_budget
)
if thinking_effort != "none":
model_args["thinking"] = ThinkingConfigAdaptiveParam(type="adaptive")
model_args["output_config"] = OutputConfigParam(effort=thinking_effort)
else:
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
model_args["temperature"] = options.get(
CONF_TEMPERATURE, DEFAULT[CONF_TEMPERATURE]
)
else:
thinking_budget = options.get(
CONF_THINKING_BUDGET, DEFAULT[CONF_THINKING_BUDGET]
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
model_args["temperature"] = options.get(
CONF_TEMPERATURE, DEFAULT[CONF_TEMPERATURE]
)
if (
not model.startswith(tuple(NON_THINKING_MODELS))
and thinking_budget >= MIN_THINKING_BUDGET
):
model_args["thinking"] = ThinkingConfigEnabledParam(
type="enabled", budget_tokens=thinking_budget
)
else:
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
model_args["temperature"] = options.get(
CONF_TEMPERATURE, DEFAULT[CONF_TEMPERATURE]
)
tools: list[ToolUnionParam] = []
if chat_log.llm_api:

View File

@@ -8,5 +8,5 @@
"documentation": "https://www.home-assistant.io/integrations/anthropic",
"integration_type": "service",
"iot_class": "cloud_polling",
"requirements": ["anthropic==0.78.0"]
"requirements": ["anthropic==0.75.0"]
}

View File

@@ -47,14 +47,12 @@
"model": {
"data": {
"thinking_budget": "[%key:component::anthropic::config_subentries::conversation::step::model::data::thinking_budget%]",
"thinking_effort": "[%key:component::anthropic::config_subentries::conversation::step::model::data::thinking_effort%]",
"user_location": "[%key:component::anthropic::config_subentries::conversation::step::model::data::user_location%]",
"web_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_search%]",
"web_search_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data::web_search_max_uses%]"
},
"data_description": {
"thinking_budget": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::thinking_budget%]",
"thinking_effort": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::thinking_effort%]",
"user_location": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::user_location%]",
"web_search": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_search%]",
"web_search_max_uses": "[%key:component::anthropic::config_subentries::conversation::step::model::data_description::web_search_max_uses%]"
@@ -97,14 +95,12 @@
"model": {
"data": {
"thinking_budget": "Thinking budget",
"thinking_effort": "Thinking effort",
"user_location": "Include home location",
"web_search": "Enable web search",
"web_search_max_uses": "Maximum web searches"
},
"data_description": {
"thinking_budget": "The number of tokens the model can use to think about the response out of the total maximum number of tokens. Set to 1024 or greater to enable extended thinking.",
"thinking_effort": "Control how many tokens Claude uses when responding, trading off between response thoroughness and token efficiency",
"user_location": "Localize search results based on home location",
"web_search": "The web search tool gives Claude direct access to real-time web content, allowing it to answer questions with up-to-date information beyond its knowledge cutoff",
"web_search_max_uses": "Limit the number of searches performed per response"
@@ -129,16 +125,5 @@
},
"title": "Model deprecated"
}
},
"selector": {
"thinking_effort": {
"options": {
"high": "[%key:common::state::high%]",
"low": "[%key:common::state::low%]",
"max": "Max",
"medium": "[%key:common::state::medium%]",
"none": "None"
}
}
}
}

View File

@@ -50,7 +50,7 @@ class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
try:
async with asyncio.timeout(CONNECTION_TIMEOUT):
data = APCUPSdData(await aioapcaccess.request_status(host, port))
except OSError, asyncio.IncompleteReadError, TimeoutError:
except (OSError, asyncio.IncompleteReadError, TimeoutError):
errors = {"base": "cannot_connect"}
return self.async_show_form(
step_id="user", data_schema=_SCHEMA, errors=errors
@@ -77,7 +77,7 @@ class ConfigFlowHandler(ConfigFlow, domain=DOMAIN):
try:
async with asyncio.timeout(CONNECTION_TIMEOUT):
data = APCUPSdData(await aioapcaccess.request_status(host, port))
except OSError, asyncio.IncompleteReadError, TimeoutError:
except (OSError, asyncio.IncompleteReadError, TimeoutError):
errors = {"base": "cannot_connect"}
return self.async_show_form(
step_id="reconfigure", data_schema=_SCHEMA, errors=errors

View File

@@ -547,7 +547,7 @@ class APCUPSdSensor(APCUPSdEntity, SensorEntity):
try:
self._attr_native_value = dateutil.parser.parse(data)
except dateutil.parser.ParserError, OverflowError:
except (dateutil.parser.ParserError, OverflowError):
# If parsing fails we should mark it as unknown, with a log for further debugging.
_LOGGER.warning('Failed to parse date for %s: "%s"', key, data)
self._attr_native_value = None

View File

@@ -181,9 +181,9 @@ async def async_import_client_credential(
CONF_DOMAIN: domain,
CONF_CLIENT_ID: credential.client_id,
CONF_CLIENT_SECRET: credential.client_secret,
CONF_AUTH_DOMAIN: auth_domain or domain,
CONF_AUTH_DOMAIN: auth_domain if auth_domain else domain,
}
item[CONF_NAME] = credential.name or DEFAULT_IMPORT_NAME
item[CONF_NAME] = credential.name if credential.name else DEFAULT_IMPORT_NAME
await hass.data[DATA_COMPONENT].async_import_item(item)

View File

@@ -168,7 +168,7 @@ class AprilaireCoordinator(BaseDataUpdateCoordinatorProtocol):
name = data.get(Attribute.NAME) if data else None
return name or "Aprilaire"
return name if name else "Aprilaire"
def get_hw_version(self, data: dict[str, Any]) -> str:
"""Get the hardware version."""

View File

@@ -41,7 +41,7 @@ class APsystemsLocalAPIFlow(ConfigFlow, domain=DOMAIN):
)
try:
device_info = await api.get_device_info()
except TimeoutError, ClientConnectionError:
except (TimeoutError, ClientConnectionError):
errors["base"] = "cannot_connect"
else:
await self.async_set_unique_id(device_info.deviceId)

View File

@@ -64,7 +64,7 @@ class ApSystemsDataCoordinator(DataUpdateCoordinator[ApSystemsSensorData]):
async def _async_setup(self) -> None:
try:
device_info = await self.api.get_device_info()
except ConnectionError, TimeoutError:
except (ConnectionError, TimeoutError):
raise UpdateFailed from None
self.api.max_power = device_info.maxPower
self.api.min_power = device_info.minPower

View File

@@ -49,7 +49,7 @@ class ApSystemsMaxOutputNumber(ApSystemsEntity, NumberEntity):
"""Set the state with the value fetched from the inverter."""
try:
status = await self._api.get_max_power()
except TimeoutError, ClientConnectorError:
except (TimeoutError, ClientConnectorError):
self._attr_available = False
else:
self._attr_available = True

View File

@@ -43,7 +43,7 @@ class ApSystemsInverterSwitch(ApSystemsEntity, SwitchEntity):
"""Update switch status and availability."""
try:
status = await self._api.get_device_power_status()
except TimeoutError, ClientConnectionError, InverterReturnedError:
except (TimeoutError, ClientConnectionError, InverterReturnedError):
self._attr_available = False
else:
self._attr_available = True

View File

@@ -56,7 +56,7 @@ class AquaCellConfigFlow(ConfigFlow, domain=DOMAIN):
refresh_token = await api.authenticate(
user_input[CONF_EMAIL], user_input[CONF_PASSWORD]
)
except ApiException, TimeoutError:
except (ApiException, TimeoutError):
errors["base"] = "cannot_connect"
except AuthenticationFailed:
errors["base"] = "invalid_auth"

View File

@@ -94,7 +94,7 @@ def _retry[_SharpAquosTVDeviceT: SharpAquosTVDevice, **_P](
try:
func(obj, *args, **kwargs)
break
except OSError, TypeError, ValueError:
except (OSError, TypeError, ValueError):
update_retries -= 1
if update_retries == 0:
obj.set_state(MediaPlayerState.OFF)

View File

@@ -201,5 +201,5 @@ class ArwnSensor(SensorEntity):
ev: dict[str, Any] = {}
ev.update(event)
self._attr_extra_state_attributes = ev
self._attr_native_value = ev.get(self._state_key)
self._attr_native_value = ev.get(self._state_key, None)
self.async_write_ha_state()

View File

@@ -969,7 +969,7 @@ class PipelineRun:
metadata,
self._speech_to_text_stream(audio_stream=stream, stt_vad=stt_vad),
)
except asyncio.CancelledError, TimeoutError:
except (asyncio.CancelledError, TimeoutError):
raise # expected
except hass_nabucasa.auth.Unauthenticated as src_error:
raise SpeechToTextError(

View File

@@ -189,7 +189,7 @@ class AsusWrtFlowHandler(ConfigFlow, domain=DOMAIN):
try:
await api.async_connect()
except AsusRouterError, OSError:
except (AsusRouterError, OSError):
_LOGGER.error(
"Error connecting to the AsusWrt router at %s using protocol %s",
host,

View File

@@ -51,5 +51,5 @@ class AtagConfigFlow(ConfigFlow, domain=DOMAIN):
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(DATA_SCHEMA),
errors=errors or {},
errors=errors if errors else {},
)

View File

@@ -304,7 +304,7 @@ async def _try_async_validate_config_item(
"""Validate config item."""
try:
return await _async_validate_config_item(hass, config, False, True)
except vol.Invalid, HomeAssistantError:
except (vol.Invalid, HomeAssistantError):
return None

View File

@@ -44,7 +44,7 @@ async def async_get_config_entry_diagnostics(
account_data["allowed"], TO_REDACT_ACCOUNT_DATA_ALLOWED
)
except AttributeError, TypeError, ValueError, KeyError:
except (AttributeError, TypeError, ValueError, KeyError):
bucket_info = {"name": "unknown", "id": "unknown"}
account_data = {"error": "Failed to retrieve detailed account information"}

View File

@@ -145,7 +145,7 @@ class BeoConfigFlowHandler(ConfigFlow, domain=DOMAIN):
async with self._client:
try:
await self._client.get_beolink_self(_request_timeout=3)
except ClientConnectorError, TimeoutError:
except (ClientConnectorError, TimeoutError):
return self.async_abort(reason="invalid_address")
self._model = discovery_info.hostname[:-16].replace("-", " ")

View File

@@ -80,16 +80,18 @@ SWITCHES = (
key=PLUG_AND_CHARGE,
translation_key=PLUG_AND_CHARGE,
function=set_plug_and_charge,
turn_on_off_fn=lambda evse_id, connector: update_on_value_and_activity(
PLUG_AND_CHARGE, evse_id, connector
turn_on_off_fn=lambda evse_id, connector: (
update_on_value_and_activity(PLUG_AND_CHARGE, evse_id, connector)
),
),
BlueCurrentSwitchEntityDescription(
key=LINKED_CHARGE_CARDS,
translation_key=LINKED_CHARGE_CARDS,
function=set_linked_charge_cards,
turn_on_off_fn=lambda evse_id, connector: update_on_value_and_activity(
PUBLIC_CHARGING, evse_id, connector, reverse_is_on=True
turn_on_off_fn=lambda evse_id, connector: (
update_on_value_and_activity(
PUBLIC_CHARGING, evse_id, connector, reverse_is_on=True
)
),
),
BlueCurrentSwitchEntityDescription(

View File

@@ -148,10 +148,8 @@ SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = (
device_class=BinarySensorDeviceClass.LOCK,
# device class lock: On means unlocked, Off means locked
# Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
value_fn=lambda v: (
v.doors_and_windows.door_lock_state
not in {LockState.LOCKED, LockState.SECURED}
),
value_fn=lambda v: v.doors_and_windows.door_lock_state
not in {LockState.LOCKED, LockState.SECURED},
attr_fn=lambda v, u: {
"door_lock_state": v.doors_and_windows.door_lock_state.value
},
@@ -191,11 +189,9 @@ SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = (
BMWBinarySensorEntityDescription(
key="is_pre_entry_climatization_enabled",
translation_key="is_pre_entry_climatization_enabled",
value_fn=lambda v: (
v.charging_profile.is_pre_entry_climatization_enabled
if v.charging_profile
else False
),
value_fn=lambda v: v.charging_profile.is_pre_entry_climatization_enabled
if v.charging_profile
else False,
is_available=lambda v: v.has_electric_drivetrain,
),
)

View File

@@ -40,9 +40,7 @@ BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = (
BMWButtonEntityDescription(
key="light_flash",
translation_key="light_flash",
remote_function=lambda vehicle: (
vehicle.remote_services.trigger_remote_light_flash()
),
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_light_flash(),
),
BMWButtonEntityDescription(
key="sound_horn",
@@ -52,24 +50,18 @@ BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = (
BMWButtonEntityDescription(
key="activate_air_conditioning",
translation_key="activate_air_conditioning",
remote_function=lambda vehicle: (
vehicle.remote_services.trigger_remote_air_conditioning()
),
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning(),
),
BMWButtonEntityDescription(
key="deactivate_air_conditioning",
translation_key="deactivate_air_conditioning",
remote_function=lambda vehicle: (
vehicle.remote_services.trigger_remote_air_conditioning_stop()
),
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning_stop(),
is_available=lambda vehicle: vehicle.is_remote_climate_stop_enabled,
),
BMWButtonEntityDescription(
key="find_vehicle",
translation_key="find_vehicle",
remote_function=lambda vehicle: (
vehicle.remote_services.trigger_remote_vehicle_finder()
),
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_vehicle_finder(),
),
)

View File

@@ -50,9 +50,7 @@ NUMBER_TYPES: list[BMWSwitchEntityDescription] = [
is_available=lambda v: v.is_remote_climate_stop_enabled,
value_fn=lambda v: v.climate.is_climate_on,
remote_service_on=lambda v: v.remote_services.trigger_remote_air_conditioning(),
remote_service_off=lambda v: (
v.remote_services.trigger_remote_air_conditioning_stop()
),
remote_service_off=lambda v: v.remote_services.trigger_remote_air_conditioning_stop(),
),
BMWSwitchEntityDescription(
key="charging",

View File

@@ -200,7 +200,7 @@ class BraviaTVCoordinator(DataUpdateCoordinator[None]):
"device": self.config_entry.title,
},
) from err
except BraviaConnectionError, BraviaConnectionTimeout, BraviaTurnedOff:
except (BraviaConnectionError, BraviaConnectionTimeout, BraviaTurnedOff):
self.is_on = False
self.connected = False
_LOGGER.debug(

View File

@@ -63,9 +63,9 @@ SENSOR_DESCRIPTIONS: tuple[BringSensorEntityDescription, ...] = (
key=BringSensor.LIST_LANGUAGE,
translation_key=BringSensor.LIST_LANGUAGE,
value_fn=(
lambda lst, settings: (
x.lower() if (x := list_language(lst.lst.listUuid, settings)) else None
)
lambda lst, settings: x.lower()
if (x := list_language(lst.lst.listUuid, settings))
else None
),
entity_category=EntityCategory.DIAGNOSTIC,
options=[x.lower() for x in BRING_SUPPORTED_LOCALES],

View File

@@ -173,7 +173,7 @@ class BroadlinkDevice[_ApiT: blk.Device = blk.Device]:
request = partial(function, *args, **kwargs)
try:
return await self.hass.async_add_executor_job(request)
except AuthorizationError, ConnectionClosedError:
except (AuthorizationError, ConnectionClosedError):
if not await self.async_auth():
raise
return await self.hass.async_add_executor_job(request)

View File

@@ -337,7 +337,7 @@ class BroadlinkRemote(BroadlinkEntity, RemoteEntity, RestoreEntity):
await asyncio.sleep(1)
try:
code = await device.async_request(device.api.check_data)
except ReadError, StorageError:
except (ReadError, StorageError):
continue
return b64encode(code).decode("utf8")
@@ -413,7 +413,7 @@ class BroadlinkRemote(BroadlinkEntity, RemoteEntity, RestoreEntity):
await asyncio.sleep(1)
try:
code = await device.async_request(device.api.check_data)
except ReadError, StorageError:
except (ReadError, StorageError):
continue
return b64encode(code).decode("utf8")

View File

@@ -127,7 +127,7 @@ class BrotherConfigFlow(ConfigFlow, domain=DOMAIN):
model, serial = await validate_input(self.hass, user_input)
except InvalidHost:
errors[CONF_HOST] = "wrong_host"
except ConnectionError, TimeoutError:
except (ConnectionError, TimeoutError):
errors["base"] = "cannot_connect"
except SnmpError:
errors["base"] = "snmp_error"
@@ -163,7 +163,7 @@ class BrotherConfigFlow(ConfigFlow, domain=DOMAIN):
await self.brother.async_update()
except UnsupportedModelError:
return self.async_abort(reason="unsupported_model")
except ConnectionError, SnmpError, TimeoutError:
except (ConnectionError, SnmpError, TimeoutError):
return self.async_abort(reason="cannot_connect")
# Check if already configured
@@ -211,7 +211,7 @@ class BrotherConfigFlow(ConfigFlow, domain=DOMAIN):
await validate_input(self.hass, user_input, entry.unique_id)
except InvalidHost:
errors[CONF_HOST] = "wrong_host"
except ConnectionError, TimeoutError:
except (ConnectionError, TimeoutError):
errors["base"] = "cannot_connect"
except SnmpError:
errors["base"] = "snmp_error"

View File

@@ -81,7 +81,7 @@ def get_event_types_by_event_class(event_class: str) -> set[str]:
but if there is only one button then it will be
button without a number postfix.
"""
return EVENT_TYPES_BY_EVENT_CLASS.get(event_class.split("_", maxsplit=1)[0], set())
return EVENT_TYPES_BY_EVENT_CLASS.get(event_class.split("_")[0], set())
async def async_validate_trigger_config(

View File

@@ -199,7 +199,7 @@ class BrData:
"""Return the temperature, or None."""
try:
return float(self.data.get(TEMPERATURE))
except ValueError, TypeError:
except (ValueError, TypeError):
return None
@property
@@ -207,7 +207,7 @@ class BrData:
"""Return the feeltemperature, or None."""
try:
return float(self.data.get(FEELTEMPERATURE))
except ValueError, TypeError:
except (ValueError, TypeError):
return None
@property
@@ -215,7 +215,7 @@ class BrData:
"""Return the pressure, or None."""
try:
return float(self.data.get(PRESSURE))
except ValueError, TypeError:
except (ValueError, TypeError):
return None
@property
@@ -223,7 +223,7 @@ class BrData:
"""Return the humidity, or None."""
try:
return int(self.data.get(HUMIDITY))
except ValueError, TypeError:
except (ValueError, TypeError):
return None
@property
@@ -231,7 +231,7 @@ class BrData:
"""Return the visibility, or None."""
try:
return int(self.data.get(VISIBILITY))
except ValueError, TypeError:
except (ValueError, TypeError):
return None
@property
@@ -239,7 +239,7 @@ class BrData:
"""Return the windgust, or None."""
try:
return float(self.data.get(WINDGUST))
except ValueError, TypeError:
except (ValueError, TypeError):
return None
@property
@@ -247,7 +247,7 @@ class BrData:
"""Return the windspeed, or None."""
try:
return float(self.data.get(WINDSPEED))
except ValueError, TypeError:
except (ValueError, TypeError):
return None
@property
@@ -255,7 +255,7 @@ class BrData:
"""Return the wind bearing, or None."""
try:
return int(self.data.get(WINDAZIMUTH))
except ValueError, TypeError:
except (ValueError, TypeError):
return None
@property

View File

@@ -691,7 +691,7 @@ class CalendarEventView(http.HomeAssistantView):
try:
start_date = dt_util.parse_datetime(start)
end_date = dt_util.parse_datetime(end)
except ValueError, AttributeError:
except (ValueError, AttributeError):
return web.Response(status=HTTPStatus.BAD_REQUEST)
if start_date is None or end_date is None:
return web.Response(status=HTTPStatus.BAD_REQUEST)

View File

@@ -58,14 +58,14 @@
"name": "Enable motion detection"
},
"play_stream": {
"description": "Plays a camera stream on a supported media player.",
"description": "Plays the camera stream on a supported media player.",
"fields": {
"format": {
"description": "Stream format supported by the media player.",
"name": "Format"
},
"media_player": {
"description": "Media player to stream to.",
"description": "Media players to stream to.",
"name": "Media player"
}
},

View File

@@ -71,7 +71,7 @@ class CanaryConfigFlow(ConfigFlow, domain=DOMAIN):
await self.hass.async_add_executor_job(
validate_input, self.hass, user_input
)
except ConnectTimeout, HTTPError:
except (ConnectTimeout, HTTPError):
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected exception")

View File

@@ -373,7 +373,7 @@ class CloudAlexaConfig(alexa_config.AbstractConfig):
if self.should_report_state:
try:
await self.async_enable_proactive_mode()
except alexa_errors.NoTokenAvailable, alexa_errors.RequireRelink:
except (alexa_errors.NoTokenAvailable, alexa_errors.RequireRelink):
await self.set_authorized(False)
else:
await self.async_disable_proactive_mode()

View File

@@ -187,7 +187,7 @@ class CloudClient(Interface):
err,
)
async_call_later(self._hass, 30, enable_alexa_job)
except alexa_errors.NoTokenAvailable, alexa_errors.RequireRelink:
except (alexa_errors.NoTokenAvailable, alexa_errors.RequireRelink):
pass
enable_alexa_job = HassJob(enable_alexa, cancel_on_shutdown=True)

View File

@@ -2,7 +2,7 @@
import base64
from collections.abc import AsyncGenerator, Callable, Iterable
from enum import StrEnum
from enum import Enum
import json
import logging
import re
@@ -59,7 +59,7 @@ _LOGGER = logging.getLogger(__name__)
_MAX_TOOL_ITERATIONS = 10
class ResponseItemType(StrEnum):
class ResponseItemType(str, Enum):
"""Response item types."""
FUNCTION_CALL = "function_call"

View File

@@ -779,7 +779,7 @@ async def websocket_update_prefs(
msg["id"], "alexa_timeout", "Timeout validating Alexa access token."
)
return
except alexa_errors.NoTokenAvailable, alexa_errors.RequireRelink:
except (alexa_errors.NoTokenAvailable, alexa_errors.RequireRelink):
connection.send_error(
msg["id"],
"alexa_relink",

View File

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

View File

@@ -196,46 +196,44 @@ class R2BackupAgent(BackupAgent):
)
upload_id = multipart_upload["UploadId"]
try:
parts: list[dict[str, Any]] = []
parts = []
part_number = 1
buffer = bytearray() # bytes buffer to store the data
buffer_size = 0 # bytes
buffer: list[bytes] = []
stream = await open_stream()
async for chunk in stream:
buffer.extend(chunk)
# upload parts of exactly MULTIPART_MIN_PART_SIZE_BYTES to ensure
# all non-trailing parts have the same size (required by S3/R2)
while len(buffer) >= MULTIPART_MIN_PART_SIZE_BYTES:
part_data = bytes(buffer[:MULTIPART_MIN_PART_SIZE_BYTES])
del buffer[:MULTIPART_MIN_PART_SIZE_BYTES]
buffer_size += len(chunk)
buffer.append(chunk)
# If buffer size meets minimum part size, upload it as a part
if buffer_size >= MULTIPART_MIN_PART_SIZE_BYTES:
_LOGGER.debug(
"Uploading part number %d, size %d",
part_number,
len(part_data),
"Uploading part number %d, size %d", part_number, buffer_size
)
part = await self._client.upload_part(
Bucket=self._bucket,
Key=self._with_prefix(tar_filename),
PartNumber=part_number,
UploadId=upload_id,
Body=part_data,
Body=b"".join(buffer),
)
parts.append({"PartNumber": part_number, "ETag": part["ETag"]})
part_number += 1
buffer_size = 0
buffer = []
# Upload the final buffer as the last part (no minimum size requirement)
if buffer:
_LOGGER.debug(
"Uploading final part number %d, size %d", part_number, len(buffer)
"Uploading final part number %d, size %d", part_number, buffer_size
)
part = await self._client.upload_part(
Bucket=self._bucket,
Key=self._with_prefix(tar_filename),
PartNumber=part_number,
UploadId=upload_id,
Body=bytes(buffer),
Body=b"".join(buffer),
)
parts.append({"PartNumber": part_number, "ETag": part["ETag"]})

View File

@@ -126,5 +126,5 @@ class ComedHourlyPricingSensor(SensorEntity):
except (TimeoutError, aiohttp.ClientError) as err:
_LOGGER.error("Could not get data from ComEd API: %s", err)
except ValueError, KeyError:
except (ValueError, KeyError):
_LOGGER.warning("Could not update status for %s", self.name)

View File

@@ -186,7 +186,7 @@ class CompensationSensor(SensorEntity):
y_value = self._poly(x_value)
self._attr_native_value = round(y_value, self._precision)
except ValueError, TypeError:
except (ValueError, TypeError):
self._attr_native_value = None
if self._source_attribute:
_LOGGER.warning(

View File

@@ -421,7 +421,7 @@ def config_entries_flow_subscribe(
config_entries.SOURCE_USER,
)
]
except ValueError, TypeError:
except (ValueError, TypeError):
# If we can't serialize, we'll filter out unserializable flows
serialized_flows = []
for flw in hass.config_entries.flow.async_progress():
@@ -434,7 +434,7 @@ def config_entries_flow_subscribe(
serialized_flows.append(
json_bytes({"type": None, "flow_id": flw["flow_id"], "flow": flw})
)
except ValueError, TypeError:
except (ValueError, TypeError):
_LOGGER.error(
"Unable to serialize to JSON. Bad data found at %s",
format_unserializable_data(

View File

@@ -38,8 +38,6 @@ CONTROL4_CURRENT_TEMPERATURE = "TEMPERATURE_F"
CONTROL4_HUMIDITY = "HUMIDITY"
CONTROL4_COOL_SETPOINT = "COOL_SETPOINT_F"
CONTROL4_HEAT_SETPOINT = "HEAT_SETPOINT_F"
CONTROL4_FAN_MODE = "FAN_MODE"
CONTROL4_FAN_MODES_LIST = "FAN_MODES_LIST"
VARIABLES_OF_INTEREST = {
CONTROL4_HVAC_STATE,
@@ -48,8 +46,6 @@ VARIABLES_OF_INTEREST = {
CONTROL4_HUMIDITY,
CONTROL4_COOL_SETPOINT,
CONTROL4_HEAT_SETPOINT,
CONTROL4_FAN_MODE,
CONTROL4_FAN_MODES_LIST,
}
# Map Control4 HVAC modes to Home Assistant
@@ -157,7 +153,12 @@ class Control4Climate(Control4Entity, ClimateEntity):
_attr_has_entity_name = True
_attr_temperature_unit = UnitOfTemperature.FAHRENHEIT
_attr_translation_key = "thermostat"
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TURN_ON
| ClimateEntityFeature.TURN_OFF
)
_attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT, HVACMode.COOL, HVACMode.HEAT_COOL]
def __init__(
@@ -200,19 +201,6 @@ class Control4Climate(Control4Entity, ClimateEntity):
"""Return the thermostat data from the coordinator."""
return self.coordinator.data.get(self._idx)
@property
def supported_features(self) -> ClimateEntityFeature:
"""Return the list of supported features."""
features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
| ClimateEntityFeature.TURN_ON
| ClimateEntityFeature.TURN_OFF
)
if self.fan_modes:
features |= ClimateEntityFeature.FAN_MODE
return features
@property
def current_temperature(self) -> float | None:
"""Return the current temperature."""
@@ -287,28 +275,6 @@ class Control4Climate(Control4Entity, ClimateEntity):
return data.get(CONTROL4_HEAT_SETPOINT)
return None
@property
def fan_mode(self) -> str | None:
"""Return the current fan mode."""
data = self._thermostat_data
if data is None:
return None
c4_fan_mode = data.get(CONTROL4_FAN_MODE)
if c4_fan_mode is None:
return None
return c4_fan_mode.lower()
@property
def fan_modes(self) -> list[str] | None:
"""Return the list of available fan modes."""
data = self._thermostat_data
if data is None:
return None
modes = data.get(CONTROL4_FAN_MODES_LIST)
if not modes:
return None
return [m.strip().lower() for m in modes.split(",") if m.strip()]
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target HVAC mode."""
c4_hvac_mode = HA_TO_C4_HVAC_MODE[hvac_mode]
@@ -337,9 +303,3 @@ class Control4Climate(Control4Entity, ClimateEntity):
await c4_climate.setHeatSetpointF(temp)
await self.coordinator.async_request_refresh()
async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set new target fan mode."""
c4_climate = self._create_api_object()
await c4_climate.setFanMode(fan_mode.title())
await self.coordinator.async_request_refresh()

View File

@@ -74,7 +74,7 @@ class Control4ConfigFlow(ConfigFlow, domain=DOMAIN):
director_bearer_token = (
await account.getDirectorBearerToken(controller_unique_id)
)["token"]
except BadCredentials, Unauthorized:
except (BadCredentials, Unauthorized):
errors["base"] = "invalid_auth"
return errors, data, description_placeholders
except NotFound:
@@ -97,7 +97,7 @@ class Control4ConfigFlow(ConfigFlow, domain=DOMAIN):
except Unauthorized:
errors["base"] = "director_auth_failed"
return errors, data, description_placeholders
except ClientError, TimeoutError:
except (ClientError, TimeoutError):
errors["base"] = "cannot_connect"
description_placeholders["host"] = host
return errors, data, description_placeholders

View File

@@ -1,15 +0,0 @@
{
"entity": {
"climate": {
"thermostat": {
"state_attributes": {
"fan_mode": {
"state": {
"circulate": "mdi:fan-clock"
}
}
}
}
}
}
}

View File

@@ -21,19 +21,6 @@
}
}
},
"entity": {
"climate": {
"thermostat": {
"state_attributes": {
"fan_mode": {
"state": {
"circulate": "Circulate"
}
}
}
}
}
},
"options": {
"step": {
"init": {

View File

@@ -48,11 +48,9 @@ SENSOR_DESCRIPTIONS: tuple[CookidooSensorEntityDescription, ...] = (
key=CookidooSensor.SUBSCRIPTION,
translation_key=CookidooSensor.SUBSCRIPTION,
value_fn=(
lambda data: (
SUBSCRIPTION_MAP[data.subscription.type]
if data.subscription
else SUBSCRIPTION_MAP["NONE"]
)
lambda data: SUBSCRIPTION_MAP[data.subscription.type]
if data.subscription
else SUBSCRIPTION_MAP["NONE"]
),
entity_category=EntityCategory.DIAGNOSTIC,
options=list(SUBSCRIPTION_MAP.values()),
@@ -62,11 +60,9 @@ SENSOR_DESCRIPTIONS: tuple[CookidooSensorEntityDescription, ...] = (
key=CookidooSensor.EXPIRES,
translation_key=CookidooSensor.EXPIRES,
value_fn=(
lambda data: (
dt_util.parse_datetime(data.subscription.expires)
if data.subscription
else None
)
lambda data: dt_util.parse_datetime(data.subscription.expires)
if data.subscription
else None
),
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.TIMESTAMP,

View File

@@ -93,7 +93,7 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
password=password,
ssl_context=client_context_no_verify(),
)
except TimeoutError, ClientError:
except (TimeoutError, ClientError):
self.host = None
return self.async_show_form(
step_id="user",

View File

@@ -142,7 +142,7 @@ async def validate_datadog_connection(
try:
client = DogStatsd(user_input[CONF_HOST], user_input[CONF_PORT])
await hass.async_add_executor_job(client.increment, "connection_test")
except OSError, ValueError:
except (OSError, ValueError):
return False
else:
return True

View File

@@ -100,7 +100,7 @@ class DeconzFlowHandler(ConfigFlow, domain=DOMAIN):
async with asyncio.timeout(10):
self.bridges = await deconz_discovery(session)
except TimeoutError, ResponseError:
except (TimeoutError, ResponseError):
self.bridges = []
if LOGGER.isEnabledFor(logging.DEBUG):
@@ -158,7 +158,7 @@ class DeconzFlowHandler(ConfigFlow, domain=DOMAIN):
except LinkButtonNotPressed:
errors["base"] = "linking_not_possible"
except ResponseError, RequestError, TimeoutError:
except (ResponseError, RequestError, TimeoutError):
errors["base"] = "no_key"
else:

View File

@@ -87,7 +87,7 @@ class DelugeFlowHandler(ConfigFlow, domain=DOMAIN):
)
try:
await self.hass.async_add_executor_job(api.connect)
except ConnectionRefusedError, TimeoutError, SSLError:
except (ConnectionRefusedError, TimeoutError, SSLError):
return "cannot_connect"
except Exception as ex:
_LOGGER.exception("Unexpected error")

View File

@@ -46,6 +46,7 @@ async def async_setup_entry(
async_add_entities(
[
DemoLight(
available=True,
effect_list=LIGHT_EFFECT_LIST,
effect=LIGHT_EFFECT_LIST[0],
translation_key="bed_light",
@@ -54,18 +55,21 @@ async def async_setup_entry(
unique_id="light_1",
),
DemoLight(
available=True,
ct=LIGHT_TEMPS[1],
device_name="Ceiling Lights",
state=True,
unique_id="light_2",
),
DemoLight(
available=True,
hs_color=LIGHT_COLORS[1],
device_name="Kitchen Lights",
state=True,
unique_id="light_3",
),
DemoLight(
available=True,
ct=LIGHT_TEMPS[1],
device_name="Office RGBW Lights",
rgbw_color=(255, 0, 0, 255),
@@ -74,6 +78,7 @@ async def async_setup_entry(
unique_id="light_4",
),
DemoLight(
available=True,
device_name="Living Room RGBWW Lights",
rgbww_color=(255, 0, 0, 255, 0),
state=True,
@@ -81,6 +86,7 @@ async def async_setup_entry(
unique_id="light_5",
),
DemoLight(
available=True,
device_name="Entrance Color + White Lights",
hs_color=LIGHT_COLORS[1],
state=True,
@@ -106,6 +112,7 @@ class DemoLight(LightEntity):
unique_id: str,
device_name: str,
state: bool,
available: bool = False,
brightness: int = 180,
ct: int | None = None,
effect_list: list[str] | None = None,
@@ -118,72 +125,128 @@ class DemoLight(LightEntity):
) -> None:
"""Initialize the light."""
self._attr_translation_key = translation_key
self._attr_brightness = brightness
self._attr_color_temp_kelvin = ct or random.choice(LIGHT_TEMPS)
self._attr_effect = effect
self._attr_effect_list = effect_list
self._attr_hs_color = hs_color
self._attr_rgbw_color = rgbw_color
self._attr_rgbww_color = rgbww_color
self._attr_is_on = state
self._attr_unique_id = unique_id
self._available = True
self._brightness = brightness
self._ct = ct or random.choice(LIGHT_TEMPS)
self._effect = effect
self._effect_list = effect_list
self._hs_color = hs_color
self._rgbw_color = rgbw_color
self._rgbww_color = rgbww_color
self._state = state
self._unique_id = unique_id
if hs_color:
self._attr_color_mode = ColorMode.HS
self._color_mode = ColorMode.HS
elif rgbw_color:
self._attr_color_mode = ColorMode.RGBW
self._color_mode = ColorMode.RGBW
elif rgbww_color:
self._attr_color_mode = ColorMode.RGBWW
self._color_mode = ColorMode.RGBWW
else:
self._attr_color_mode = ColorMode.COLOR_TEMP
self._color_mode = ColorMode.COLOR_TEMP
if not supported_color_modes:
supported_color_modes = SUPPORT_DEMO
self._attr_supported_color_modes = supported_color_modes
if self._attr_effect_list is not None:
self._color_modes = supported_color_modes
if self._effect_list is not None:
self._attr_supported_features |= LightEntityFeature.EFFECT
self._attr_device_info = DeviceInfo(
identifiers={
# Serial numbers are unique identifiers within a specific domain
(DOMAIN, unique_id)
(DOMAIN, self.unique_id)
},
name=device_name,
)
@property
def unique_id(self) -> str:
"""Return unique ID for light."""
return self._unique_id
@property
def available(self) -> bool:
"""Return availability."""
# This demo light is always available, but well-behaving components
# should implement this to inform Home Assistant accordingly.
return True
return self._available
@property
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
return self._brightness
@property
def color_mode(self) -> ColorMode | None:
"""Return the color mode of the light."""
return self._color_mode
@property
def hs_color(self) -> tuple[int, int] | None:
"""Return the hs color value."""
return self._hs_color
@property
def rgbw_color(self) -> tuple[int, int, int, int] | None:
"""Return the rgbw color value."""
return self._rgbw_color
@property
def rgbww_color(self) -> tuple[int, int, int, int, int] | None:
"""Return the rgbww color value."""
return self._rgbww_color
@property
def color_temp_kelvin(self) -> int | None:
"""Return the color temperature value in Kelvin."""
return self._ct
@property
def effect_list(self) -> list[str] | None:
"""Return the list of supported effects."""
return self._effect_list
@property
def effect(self) -> str | None:
"""Return the current effect."""
return self._effect
@property
def is_on(self) -> bool:
"""Return true if light is on."""
return self._state
@property
def supported_color_modes(self) -> set[ColorMode]:
"""Flag supported color modes."""
return self._color_modes
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on."""
self._attr_is_on = True
self._state = True
if ATTR_BRIGHTNESS in kwargs:
self._attr_brightness = kwargs[ATTR_BRIGHTNESS]
self._brightness = kwargs[ATTR_BRIGHTNESS]
if ATTR_COLOR_TEMP_KELVIN in kwargs:
self._attr_color_mode = ColorMode.COLOR_TEMP
self._attr_color_temp_kelvin = kwargs[ATTR_COLOR_TEMP_KELVIN]
self._color_mode = ColorMode.COLOR_TEMP
self._ct = kwargs[ATTR_COLOR_TEMP_KELVIN]
if ATTR_EFFECT in kwargs:
self._attr_effect = kwargs[ATTR_EFFECT]
self._effect = kwargs[ATTR_EFFECT]
if ATTR_HS_COLOR in kwargs:
self._attr_color_mode = ColorMode.HS
self._attr_hs_color = kwargs[ATTR_HS_COLOR]
self._color_mode = ColorMode.HS
self._hs_color = kwargs[ATTR_HS_COLOR]
if ATTR_RGBW_COLOR in kwargs:
self._attr_color_mode = ColorMode.RGBW
self._attr_rgbw_color = kwargs[ATTR_RGBW_COLOR]
self._color_mode = ColorMode.RGBW
self._rgbw_color = kwargs[ATTR_RGBW_COLOR]
if ATTR_RGBWW_COLOR in kwargs:
self._attr_color_mode = ColorMode.RGBWW
self._attr_rgbww_color = kwargs[ATTR_RGBWW_COLOR]
self._color_mode = ColorMode.RGBWW
self._rgbww_color = kwargs[ATTR_RGBWW_COLOR]
if ATTR_WHITE in kwargs:
self._attr_color_mode = ColorMode.WHITE
self._attr_brightness = kwargs[ATTR_WHITE]
self._color_mode = ColorMode.WHITE
self._brightness = kwargs[ATTR_WHITE]
# As we have disabled polling, we need to inform
# Home Assistant about updates in our state ourselves.
@@ -191,7 +254,7 @@ class DemoLight(LightEntity):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
self._attr_is_on = False
self._state = False
# As we have disabled polling, we need to inform
# Home Assistant about updates in our state ourselves.

View File

@@ -199,7 +199,7 @@ class DenonAvrFlowHandler(ConfigFlow, domain=DOMAIN):
try:
success = await connect_denonavr.async_connect_receiver()
except AvrNetworkError, AvrTimoutError:
except (AvrNetworkError, AvrTimoutError):
success = False
if not success:
return self.async_abort(reason="cannot_connect")

View File

@@ -7,7 +7,7 @@
"integration_type": "device",
"iot_class": "local_push",
"loggers": ["denonavr"],
"requirements": ["denonavr==1.3.2"],
"requirements": ["denonavr==1.3.1"],
"ssdp": [
{
"deviceType": "urn:schemas-upnp-org:device:MediaRenderer:1",

View File

@@ -17,7 +17,6 @@ from denonavr.const import (
STATE_ON,
STATE_PAUSED,
STATE_PLAYING,
STATE_STOPPED,
)
from denonavr.exceptions import (
AvrCommandError,
@@ -70,7 +69,6 @@ SUPPORT_MEDIA_MODES = (
| MediaPlayerEntityFeature.NEXT_TRACK
| MediaPlayerEntityFeature.VOLUME_SET
| MediaPlayerEntityFeature.PLAY
| MediaPlayerEntityFeature.STOP
)
SCAN_INTERVAL = timedelta(seconds=10)
@@ -98,7 +96,6 @@ DENON_STATE_MAPPING = {
STATE_OFF: MediaPlayerState.OFF,
STATE_PLAYING: MediaPlayerState.PLAYING,
STATE_PAUSED: MediaPlayerState.PAUSED,
STATE_STOPPED: MediaPlayerState.IDLE,
}
@@ -407,11 +404,6 @@ class DenonDevice(MediaPlayerEntity):
"""Send pause command."""
await self._receiver.async_pause()
@async_log_errors
async def async_media_stop(self) -> None:
"""Send stop command."""
await self._receiver.async_stop()
@async_log_errors
async def async_media_previous_track(self) -> None:
"""Send previous track command."""

View File

@@ -104,7 +104,7 @@ PLATFORM_SCHEMA = SENSOR_PLATFORM_SCHEMA.extend(
def _is_decimal_state(state: str) -> bool:
try:
Decimal(state)
except InvalidOperation, TypeError:
except (InvalidOperation, TypeError):
return False
else:
return True
@@ -220,8 +220,8 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
if max_sub_interval is None or max_sub_interval.total_seconds() == 0
else max_sub_interval
)
self._cancel_max_sub_interval_exceeded_callback: CALLBACK_TYPE = lambda *args: (
None
self._cancel_max_sub_interval_exceeded_callback: CALLBACK_TYPE = (
lambda *args: None
)
def _derive_and_set_attributes_from_state(self, source_state: State | None) -> None:
@@ -306,7 +306,7 @@ class DerivativeSensor(RestoreSensor, SensorEntity):
Decimal(restored_data.native_value), # type: ignore[arg-type]
self._round_digits,
)
except InvalidOperation, TypeError:
except (InvalidOperation, TypeError):
self._attr_native_value = None
async def async_added_to_hass(self) -> None:

View File

@@ -86,7 +86,11 @@ class DevialetMediaPlayerEntity(
self._attr_media_position_updated_at = (
self.coordinator.client.position_updated_at
)
self._attr_media_title = self.coordinator.client.media_title or self.source
self._attr_media_title = (
self.coordinator.client.media_title
if self.coordinator.client.media_title
else self.source
)
self.async_write_ha_state()
@property

View File

@@ -311,7 +311,7 @@ async def _async_get_device_automation_capabilities(
try:
capabilities = await getattr(platform, function_name)(hass, automation)
except EntityNotFound, InvalidDeviceAutomationConfig:
except (EntityNotFound, InvalidDeviceAutomationConfig):
return {}
capabilities = capabilities.copy()

View File

@@ -895,7 +895,7 @@ class Device(RestoreEntity):
try:
self.gps = float(gps[0]), float(gps[1])
self.gps_accuracy = gps_accuracy or 0
except ValueError, TypeError, IndexError:
except (ValueError, TypeError, IndexError):
self.gps = None
self.gps_accuracy = 0
LOGGER.warning("Could not parse gps value for %s: %s", self.dev_id, gps)

View File

@@ -87,7 +87,7 @@ async def _async_try_connect(token: str) -> tuple[str | None, nextcord.AppInfo |
info = await discord_bot.application_info()
except nextcord.LoginFailure:
return "invalid_auth", None
except ClientConnectorError, nextcord.HTTPException, nextcord.NotFound:
except (ClientConnectorError, nextcord.HTTPException, nextcord.NotFound):
return "cannot_connect", None
except Exception:
_LOGGER.exception("Unexpected exception")

View File

@@ -72,7 +72,7 @@ class DiscovergyConfigFlow(ConfigFlow, domain=DOMAIN):
httpx_client=get_async_client(self.hass),
authentication=BasicAuth(),
).meters()
except discovergyError.HTTPError, discovergyError.DiscovergyClientError:
except (discovergyError.HTTPError, discovergyError.DiscovergyClientError):
errors["base"] = "cannot_connect"
except discovergyError.InvalidLogin:
errors["base"] = "invalid_auth"

View File

@@ -260,7 +260,7 @@ class DlnaDmrEntity(MediaPlayerEntity):
try:
bootid_str = info.ssdp_headers[ssdp.ATTR_SSDP_BOOTID]
bootid: int | None = int(bootid_str, 10)
except KeyError, ValueError:
except (KeyError, ValueError):
bootid = None
if change == ssdp.SsdpChange.UPDATE:

View File

@@ -234,7 +234,7 @@ class DmsDeviceSource:
try:
bootid_str = info.ssdp_headers[ssdp.ATTR_SSDP_BOOTID]
bootid: int | None = int(bootid_str, 10)
except KeyError, ValueError:
except (KeyError, ValueError):
bootid = None
if change == ssdp.SsdpChange.UPDATE:
@@ -245,7 +245,7 @@ class DmsDeviceSource:
try:
next_bootid_str = info.ssdp_headers[ssdp.ATTR_SSDP_NEXTBOOTID]
self._bootid = int(next_bootid_str, 10)
except KeyError, ValueError:
except (KeyError, ValueError):
pass
# Nothing left to do until ssdp:alive comes through
return
@@ -567,7 +567,7 @@ class DmsDeviceSource:
# can_play is False).
try:
child_count = int(item.child_count)
except AttributeError, TypeError, ValueError:
except (AttributeError, TypeError, ValueError):
child_count = 0
can_expand = (
bool(children) or child_count > 0 or isinstance(item, didl_lite.Container)

View File

@@ -36,10 +36,8 @@ BINARY_SENSOR_DESCRIPTIONS = (
key="security_locked",
translation_key="deadbolt",
device_class=BinarySensorDeviceClass.LOCK,
is_on=lambda state: (
state.unlock_status
not in (UnlockStatus.SECURITY_LOCKED, UnlockStatus.UNLOCKED_SECURITY_LOCKED)
),
is_on=lambda state: state.unlock_status
not in (UnlockStatus.SECURITY_LOCKED, UnlockStatus.UNLOCKED_SECURITY_LOCKED),
),
)

View File

@@ -40,7 +40,7 @@ class Dremel3DPrinterConfigFlow(ConfigFlow, domain=DOMAIN):
try:
api = await self.hass.async_add_executor_job(Dremel3DPrinter, host)
except ConnectTimeout, HTTPError, JSONDecodeError:
except (ConnectTimeout, HTTPError, JSONDecodeError):
errors = {"base": "cannot_connect"}
except Exception: # noqa: BLE001
LOGGER.exception("An unknown error has occurred")

View File

@@ -120,7 +120,7 @@ class DSMRConnection:
try:
transport, protocol = await asyncio.create_task(reader_factory())
except serial.SerialException, OSError:
except (serial.SerialException, OSError):
LOGGER.exception("Error connecting to DSMR")
return False

View File

@@ -837,7 +837,7 @@ async def async_setup_entry(
# throttle reconnect attempts
await asyncio.sleep(DEFAULT_RECONNECT_INTERVAL)
except serial.SerialException, OSError:
except (serial.SerialException, OSError):
# Log any error while establishing connection and drop to retry
# connection wait
LOGGER.exception("Error connecting to DSMR")

View File

@@ -44,7 +44,7 @@ class DukeEnergyConfigFlow(ConfigFlow, domain=DOMAIN):
auth = await api.authenticate()
except ClientResponseError as e:
errors["base"] = "invalid_auth" if e.status == 404 else "cannot_connect"
except ClientError, TimeoutError:
except (ClientError, TimeoutError):
errors["base"] = "cannot_connect"
except Exception:
_LOGGER.exception("Unexpected exception")

View File

@@ -214,7 +214,7 @@ class DukeEnergyCoordinator(DataUpdateCoordinator[None]):
# Make sure we don't go back too far
if end_step < start:
break
except TimeoutError, ClientError:
except (TimeoutError, ClientError):
# ClientError is raised when there is no more data for the range
break

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
from datetime import date, datetime
from enum import StrEnum
from enum import Enum
from functools import partial
from typing import Final
@@ -47,7 +47,7 @@ SERVICE_SCHEMA: Final = vol.Schema(
)
class PriceType(StrEnum):
class PriceType(str, Enum):
"""Type of price."""
ENERGY_USAGE = "energy_usage"

View File

@@ -68,7 +68,7 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool:
try:
ebusdpy.init(server_address)
except TimeoutError, OSError:
except (TimeoutError, OSError):
return False
hass.data[EBUSD_DATA] = EbusdData(server_address, circuit)
sensor_config = {

View File

@@ -619,7 +619,9 @@ class Thermostat(ClimateEntity):
return [
{
"id": device.id,
"name_by_user": device.name_by_user or device.name,
"name_by_user": device.name_by_user
if device.name_by_user
else device.name,
}
for device in device_registry.devices.values()
for sensor_info in sensors_info
@@ -922,7 +924,7 @@ class Thermostat(ClimateEntity):
sensor_names = self._sensors_in_preset_mode(preset_mode)
return sorted(
[
device.name_by_user or device.name
device.name_by_user if device.name_by_user else device.name
for device in device_registry.devices.values()
for sensor_name in sensor_names
if device.name == sensor_name

View File

@@ -212,7 +212,7 @@ def _process_forecast(json):
if json["windSpeed"] != ECOBEE_STATE_UNKNOWN:
forecast[ATTR_FORECAST_NATIVE_WIND_SPEED] = int(json["windSpeed"])
except ValueError, IndexError, KeyError:
except (ValueError, IndexError, KeyError):
return None
if forecast:

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